Packages

  • dibi
    • drivers
    • nette
    • reflection
  • None
  • PHP

Classes

  • DibiFirebirdDriver
  • DibiMsSql2005Driver
  • DibiMsSqlDriver
  • DibiMySqlDriver
  • DibiMySqliDriver
  • DibiOdbcDriver
  • DibiOracleDriver
  • DibiPdoDriver
  • DibiPostgreDriver
  • DibiSqlite3Driver
  • DibiSqliteDriver

Exceptions

  • DibiProcedureException
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the "dibi" - smart database abstraction layer.
  5:  *
  6:  * Copyright (c) 2005 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  */
 11: 
 12: 
 13: require_once dirname(__FILE__) . '/mysql.reflector.php';
 14: 
 15: 
 16: /**
 17:  * The dibi driver for MySQL database.
 18:  *
 19:  * Driver options:
 20:  *   - host => the MySQL server host name
 21:  *   - port (int) => the port number to attempt to connect to the MySQL server
 22:  *   - socket => the socket or named pipe
 23:  *   - username (or user)
 24:  *   - password (or pass)
 25:  *   - database => the database name to select
 26:  *   - flags (int) => driver specific constants (MYSQL_CLIENT_*)
 27:  *   - charset => character encoding to set (default is utf8)
 28:  *   - persistent (bool) => try to find a persistent link?
 29:  *   - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
 30:  *   - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
 31:  *   - resource (resource) => existing connection resource
 32:  *   - lazy, profiler, result, substitutes, ... => see DibiConnection options
 33:  *
 34:  * @author     David Grudl
 35:  * @package    dibi\drivers
 36:  */
 37: class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
 38: {
 39:     const ERROR_ACCESS_DENIED = 1045;
 40:     const ERROR_DUPLICATE_ENTRY = 1062;
 41:     const ERROR_DATA_TRUNCATED = 1265;
 42: 
 43:     /** @var resource  Connection resource */
 44:     private $connection;
 45: 
 46:     /** @var resource  Resultset resource */
 47:     private $resultSet;
 48: 
 49:     /** @var bool  Is buffered (seekable and countable)? */
 50:     private $buffered;
 51: 
 52: 
 53: 
 54:     /**
 55:      * @throws DibiNotSupportedException
 56:      */
 57:     public function __construct()
 58:     {
 59:         if (!extension_loaded('mysql')) {
 60:             throw new DibiNotSupportedException("PHP extension 'mysql' is not loaded.");
 61:         }
 62:     }
 63: 
 64: 
 65: 
 66:     /**
 67:      * Connects to a database.
 68:      * @return void
 69:      * @throws DibiException
 70:      */
 71:     public function connect(array &$config)
 72:     {
 73:         if (isset($config['resource'])) {
 74:             $this->connection = $config['resource'];
 75: 
 76:         } else {
 77:             // default values
 78:             DibiConnection::alias($config, 'flags', 'options');
 79:             if (!isset($config['charset'])) $config['charset'] = 'utf8';
 80:             if (!isset($config['username'])) $config['username'] = ini_get('mysql.default_user');
 81:             if (!isset($config['password'])) $config['password'] = ini_get('mysql.default_password');
 82:             if (!isset($config['host'])) {
 83:                 $host = ini_get('mysql.default_host');
 84:                 if ($host) {
 85:                     $config['host'] = $host;
 86:                     $config['port'] = ini_get('mysql.default_port');
 87:                 } else {
 88:                     if (!isset($config['socket'])) $config['socket'] = ini_get('mysql.default_socket');
 89:                     $config['host'] = NULL;
 90:                 }
 91:             }
 92: 
 93:             if (empty($config['socket'])) {
 94:                 $host = $config['host'] . (empty($config['port']) ? '' : ':' . $config['port']);
 95:             } else {
 96:                 $host = ':' . $config['socket'];
 97:             }
 98: 
 99:             if (empty($config['persistent'])) {
100:                 $this->connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['flags']); // intentionally @
101:             } else {
102:                 $this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['flags']); // intentionally @
103:             }
104:         }
105: 
106:         if (!is_resource($this->connection)) {
107:             throw new DibiDriverException(mysql_error(), mysql_errno());
108:         }
109: 
110:         if (isset($config['charset'])) {
111:             $ok = FALSE;
112:             if (function_exists('mysql_set_charset')) {
113:                 // affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.2.3)
114:                 $ok = @mysql_set_charset($config['charset'], $this->connection); // intentionally @
115:             }
116:             if (!$ok) {
117:                 $this->query("SET NAMES '$config[charset]'");
118:             }
119:         }
120: 
121:         if (isset($config['database'])) {
122:             if (!@mysql_select_db($config['database'], $this->connection)) { // intentionally @
123:                 throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
124:             }
125:         }
126: 
127:         if (isset($config['sqlmode'])) {
128:             $this->query("SET sql_mode='$config[sqlmode]'");
129:         }
130: 
131:         $this->query("SET time_zone='" . date('P') . "'");
132: 
133:         $this->buffered = empty($config['unbuffered']);
134:     }
135: 
136: 
137: 
138:     /**
139:      * Disconnects from a database.
140:      * @return void
141:      */
142:     public function disconnect()
143:     {
144:         mysql_close($this->connection);
145:     }
146: 
147: 
148: 
149:     /**
150:      * Executes the SQL query.
151:      * @param  string      SQL statement.
152:      * @return IDibiResultDriver|NULL
153:      * @throws DibiDriverException
154:      */
155:     public function query($sql)
156:     {
157:         if ($this->buffered) {
158:             $res = @mysql_query($sql, $this->connection); // intentionally @
159:         } else {
160:             $res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
161:         }
162: 
163:         if (mysql_errno($this->connection)) {
164:             throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql);
165: 
166:         } elseif (is_resource($res)) {
167:             return $this->createResultDriver($res);
168:         }
169:     }
170: 
171: 
172: 
173:     /**
174:      * Retrieves information about the most recently executed query.
175:      * @return array
176:      */
177:     public function getInfo()
178:     {
179:         $res = array();
180:         preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
181:         if (preg_last_error()) throw new DibiPcreException;
182: 
183:         foreach ($matches as $m) {
184:             $res[$m[1]] = (int) $m[2];
185:         }
186:         return $res;
187:     }
188: 
189: 
190: 
191:     /**
192:      * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
193:      * @return int|FALSE  number of rows or FALSE on error
194:      */
195:     public function getAffectedRows()
196:     {
197:         return mysql_affected_rows($this->connection);
198:     }
199: 
200: 
201: 
202:     /**
203:      * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
204:      * @return int|FALSE  int on success or FALSE on failure
205:      */
206:     public function getInsertId($sequence)
207:     {
208:         return mysql_insert_id($this->connection);
209:     }
210: 
211: 
212: 
213:     /**
214:      * Begins a transaction (if supported).
215:      * @param  string  optional savepoint name
216:      * @return void
217:      * @throws DibiDriverException
218:      */
219:     public function begin($savepoint = NULL)
220:     {
221:         $this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
222:     }
223: 
224: 
225: 
226:     /**
227:      * Commits statements in a transaction.
228:      * @param  string  optional savepoint name
229:      * @return void
230:      * @throws DibiDriverException
231:      */
232:     public function commit($savepoint = NULL)
233:     {
234:         $this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
235:     }
236: 
237: 
238: 
239:     /**
240:      * Rollback changes in a transaction.
241:      * @param  string  optional savepoint name
242:      * @return void
243:      * @throws DibiDriverException
244:      */
245:     public function rollback($savepoint = NULL)
246:     {
247:         $this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
248:     }
249: 
250: 
251: 
252:     /**
253:      * Returns the connection resource.
254:      * @return mixed
255:      */
256:     public function getResource()
257:     {
258:         return $this->connection;
259:     }
260: 
261: 
262: 
263:     /**
264:      * Returns the connection reflector.
265:      * @return IDibiReflector
266:      */
267:     public function getReflector()
268:     {
269:         return new DibiMySqlReflector($this);
270:     }
271: 
272: 
273: 
274:     /**
275:      * Result set driver factory.
276:      * @param  resource
277:      * @return IDibiResultDriver
278:      */
279:     public function createResultDriver($resource)
280:     {
281:         $res = clone $this;
282:         $res->resultSet = $resource;
283:         return $res;
284:     }
285: 
286: 
287: 
288:     /********************* SQL ****************d*g**/
289: 
290: 
291: 
292:     /**
293:      * Encodes data for use in a SQL statement.
294:      * @param  mixed     value
295:      * @param  string    type (dibi::TEXT, dibi::BOOL, ...)
296:      * @return string    encoded value
297:      * @throws InvalidArgumentException
298:      */
299:     public function escape($value, $type)
300:     {
301:         switch ($type) {
302:         case dibi::TEXT:
303:             return "'" . mysql_real_escape_string($value, $this->connection) . "'";
304: 
305:         case dibi::BINARY:
306:             return "_binary'" . mysql_real_escape_string($value, $this->connection) . "'";
307: 
308:         case dibi::IDENTIFIER:
309:             // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
310:             return '`' . str_replace('`', '``', $value) . '`';
311: 
312:         case dibi::BOOL:
313:             return $value ? 1 : 0;
314: 
315:         case dibi::DATE:
316:             return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
317: 
318:         case dibi::DATETIME:
319:             return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
320: 
321:         default:
322:             throw new InvalidArgumentException('Unsupported type.');
323:         }
324:     }
325: 
326: 
327: 
328:     /**
329:      * Encodes string for use in a LIKE statement.
330:      * @param  string
331:      * @param  int
332:      * @return string
333:      */
334:     public function escapeLike($value, $pos)
335:     {
336:         $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
337:         return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
338:     }
339: 
340: 
341: 
342:     /**
343:      * Decodes data from result set.
344:      * @param  string    value
345:      * @param  string    type (dibi::BINARY)
346:      * @return string    decoded value
347:      * @throws InvalidArgumentException
348:      */
349:     public function unescape($value, $type)
350:     {
351:         if ($type === dibi::BINARY) {
352:             return $value;
353:         }
354:         throw new InvalidArgumentException('Unsupported type.');
355:     }
356: 
357: 
358: 
359:     /**
360:      * Injects LIMIT/OFFSET to the SQL query.
361:      * @param  string &$sql  The SQL query that will be modified.
362:      * @param  int $limit
363:      * @param  int $offset
364:      * @return void
365:      */
366:     public function applyLimit(&$sql, $limit, $offset)
367:     {
368:         if ($limit < 0 && $offset < 1) return;
369: 
370:         // see http://dev.mysql.com/doc/refman/5.0/en/select.html
371:         $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
372:             . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
373:     }
374: 
375: 
376: 
377:     /********************* result set ****************d*g**/
378: 
379: 
380: 
381:     /**
382:      * Automatically frees the resources allocated for this result set.
383:      * @return void
384:      */
385:     public function __destruct()
386:     {
387:         $this->resultSet && @$this->free();
388:     }
389: 
390: 
391: 
392:     /**
393:      * Returns the number of rows in a result set.
394:      * @return int
395:      */
396:     public function getRowCount()
397:     {
398:         if (!$this->buffered) {
399:             throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
400:         }
401:         return mysql_num_rows($this->resultSet);
402:     }
403: 
404: 
405: 
406:     /**
407:      * Fetches the row at current position and moves the internal cursor to the next position.
408:      * @param  bool     TRUE for associative array, FALSE for numeric
409:      * @return array    array on success, nonarray if no next record
410:      */
411:     public function fetch($assoc)
412:     {
413:         return mysql_fetch_array($this->resultSet, $assoc ? MYSQL_ASSOC : MYSQL_NUM);
414:     }
415: 
416: 
417: 
418:     /**
419:      * Moves cursor position without fetching row.
420:      * @param  int      the 0-based cursor pos to seek to
421:      * @return boolean  TRUE on success, FALSE if unable to seek to specified record
422:      * @throws DibiException
423:      */
424:     public function seek($row)
425:     {
426:         if (!$this->buffered) {
427:             throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
428:         }
429: 
430:         return mysql_data_seek($this->resultSet, $row);
431:     }
432: 
433: 
434: 
435:     /**
436:      * Frees the resources allocated for this result set.
437:      * @return void
438:      */
439:     public function free()
440:     {
441:         mysql_free_result($this->resultSet);
442:         $this->resultSet = NULL;
443:     }
444: 
445: 
446: 
447:     /**
448:      * Returns metadata for all columns in a result set.
449:      * @return array
450:      */
451:     public function getResultColumns()
452:     {
453:         $count = mysql_num_fields($this->resultSet);
454:         $columns = array();
455:         for ($i = 0; $i < $count; $i++) {
456:             $row = (array) mysql_fetch_field($this->resultSet, $i);
457:             $columns[] = array(
458:                 'name' => $row['name'],
459:                 'table' => $row['table'],
460:                 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
461:                 'nativetype' => strtoupper($row['type']),
462:                 'vendor' => $row,
463:             );
464:         }
465:         return $columns;
466:     }
467: 
468: 
469: 
470:     /**
471:      * Returns the result set resource.
472:      * @return mixed
473:      */
474:     public function getResultResource()
475:     {
476:         return $this->resultSet;
477:     }
478: 
479: }
480: 
dibi API documentation API documentation generated by ApiGen 2.3.0