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: require_once dirname(__FILE__) . '/sqlite.reflector.php';
 15: 
 16: 
 17: /**
 18:  * The dibi driver for PDO.
 19:  *
 20:  * Driver options:
 21:  *   - dsn => driver specific DSN
 22:  *   - username (or user)
 23:  *   - password (or pass)
 24:  *   - options (array) => driver specific options {@see PDO::__construct}
 25:  *   - resource (PDO) => existing connection
 26:  *   - lazy, profiler, result, substitutes, ... => see DibiConnection options
 27:  *
 28:  * @author     David Grudl
 29:  * @package    dibi\drivers
 30:  */
 31: class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
 32: {
 33:     /** @var PDO  Connection resource */
 34:     private $connection;
 35: 
 36:     /** @var PDOStatement  Resultset resource */
 37:     private $resultSet;
 38: 
 39:     /** @var int|FALSE  Affected rows */
 40:     private $affectedRows = FALSE;
 41: 
 42:     /** @var string */
 43:     private $driverName;
 44: 
 45: 
 46: 
 47:     /**
 48:      * @throws DibiNotSupportedException
 49:      */
 50:     public function __construct()
 51:     {
 52:         if (!extension_loaded('pdo')) {
 53:             throw new DibiNotSupportedException("PHP extension 'pdo' is not loaded.");
 54:         }
 55:     }
 56: 
 57: 
 58: 
 59:     /**
 60:      * Connects to a database.
 61:      * @return void
 62:      * @throws DibiException
 63:      */
 64:     public function connect(array &$config)
 65:     {
 66:         $foo = & $config['dsn'];
 67:         $foo = & $config['options'];
 68:         DibiConnection::alias($config, 'resource', 'pdo');
 69: 
 70:         if ($config['resource'] instanceof PDO) {
 71:             $this->connection = $config['resource'];
 72: 
 73:         } else try {
 74:             $this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
 75: 
 76:         } catch (PDOException $e) {
 77:             throw new DibiDriverException($e->getMessage(), $e->getCode());
 78:         }
 79: 
 80:         if (!$this->connection) {
 81:             throw new DibiDriverException('Connecting error.');
 82:         }
 83: 
 84:         $this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
 85:     }
 86: 
 87: 
 88: 
 89:     /**
 90:      * Disconnects from a database.
 91:      * @return void
 92:      */
 93:     public function disconnect()
 94:     {
 95:         $this->connection = NULL;
 96:     }
 97: 
 98: 
 99: 
100:     /**
101:      * Executes the SQL query.
102:      * @param  string      SQL statement.
103:      * @return IDibiResultDriver|NULL
104:      * @throws DibiDriverException
105:      */
106:     public function query($sql)
107:     {
108:         // must detect if SQL returns result set or num of affected rows
109:         $cmd = strtoupper(substr(ltrim($sql), 0, 6));
110:         static $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1);
111:         $this->affectedRows = FALSE;
112: 
113:         if (isset($list[$cmd])) {
114:             $this->affectedRows = $this->connection->exec($sql);
115: 
116:             if ($this->affectedRows === FALSE) {
117:                 $err = $this->connection->errorInfo();
118:                 throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
119:             }
120: 
121:         } else {
122:             $res = $this->connection->query($sql);
123: 
124:             if ($res === FALSE) {
125:                 $err = $this->connection->errorInfo();
126:                 throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
127:             } else {
128:                 return $this->createResultDriver($res);
129:             }
130:         }
131:     }
132: 
133: 
134: 
135:     /**
136:      * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
137:      * @return int|FALSE  number of rows or FALSE on error
138:      */
139:     public function getAffectedRows()
140:     {
141:         return $this->affectedRows;
142:     }
143: 
144: 
145: 
146:     /**
147:      * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
148:      * @return int|FALSE  int on success or FALSE on failure
149:      */
150:     public function getInsertId($sequence)
151:     {
152:         return $this->connection->lastInsertId();
153:     }
154: 
155: 
156: 
157:     /**
158:      * Begins a transaction (if supported).
159:      * @param  string  optional savepoint name
160:      * @return void
161:      * @throws DibiDriverException
162:      */
163:     public function begin($savepoint = NULL)
164:     {
165:         if (!$this->connection->beginTransaction()) {
166:             $err = $this->connection->errorInfo();
167:             throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
168:         }
169:     }
170: 
171: 
172: 
173:     /**
174:      * Commits statements in a transaction.
175:      * @param  string  optional savepoint name
176:      * @return void
177:      * @throws DibiDriverException
178:      */
179:     public function commit($savepoint = NULL)
180:     {
181:         if (!$this->connection->commit()) {
182:             $err = $this->connection->errorInfo();
183:             throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
184:         }
185:     }
186: 
187: 
188: 
189:     /**
190:      * Rollback changes in a transaction.
191:      * @param  string  optional savepoint name
192:      * @return void
193:      * @throws DibiDriverException
194:      */
195:     public function rollback($savepoint = NULL)
196:     {
197:         if (!$this->connection->rollBack()) {
198:             $err = $this->connection->errorInfo();
199:             throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
200:         }
201:     }
202: 
203: 
204: 
205:     /**
206:      * Returns the connection resource.
207:      * @return PDO
208:      */
209:     public function getResource()
210:     {
211:         return $this->connection;
212:     }
213: 
214: 
215: 
216:     /**
217:      * Returns the connection reflector.
218:      * @return IDibiReflector
219:      */
220:     public function getReflector()
221:     {
222:         switch ($this->driverName) {
223:         case 'mysql':
224:             return new DibiMySqlReflector($this);
225: 
226:         case 'sqlite':
227:         case 'sqlite2':
228:             return new DibiSqliteReflector($this);
229: 
230:         default:
231:             throw new DibiNotSupportedException;
232:         }
233:     }
234: 
235: 
236: 
237:     /**
238:      * Result set driver factory.
239:      * @param  PDOStatement
240:      * @return IDibiResultDriver
241:      */
242:     public function createResultDriver(PDOStatement $resource)
243:     {
244:         $res = clone $this;
245:         $res->resultSet = $resource;
246:         return $res;
247:     }
248: 
249: 
250: 
251:     /********************* SQL ****************d*g**/
252: 
253: 
254: 
255:     /**
256:      * Encodes data for use in a SQL statement.
257:      * @param  mixed     value
258:      * @param  string    type (dibi::TEXT, dibi::BOOL, ...)
259:      * @return string    encoded value
260:      * @throws InvalidArgumentException
261:      */
262:     public function escape($value, $type)
263:     {
264:         switch ($type) {
265:         case dibi::TEXT:
266:             return $this->connection->quote($value, PDO::PARAM_STR);
267: 
268:         case dibi::BINARY:
269:             return $this->connection->quote($value, PDO::PARAM_LOB);
270: 
271:         case dibi::IDENTIFIER:
272:             switch ($this->driverName) {
273:             case 'mysql':
274:                 return '`' . str_replace('`', '``', $value) . '`';
275: 
276:             case 'pgsql':
277:                 return '"' . str_replace('"', '""', $value) . '"';
278: 
279:             case 'sqlite':
280:             case 'sqlite2':
281:                 return '[' . strtr($value, '[]', '  ') . ']';
282: 
283:             case 'odbc':
284:             case 'oci': // TODO: not tested
285:             case 'mssql':
286:                 return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
287: 
288:             default:
289:                 return $value;
290:             }
291: 
292:         case dibi::BOOL:
293:             return $this->connection->quote($value, PDO::PARAM_BOOL);
294: 
295:         case dibi::DATE:
296:             return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
297: 
298:         case dibi::DATETIME:
299:             return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
300: 
301:         default:
302:             throw new InvalidArgumentException('Unsupported type.');
303:         }
304:     }
305: 
306: 
307: 
308:     /**
309:      * Encodes string for use in a LIKE statement.
310:      * @param  string
311:      * @param  int
312:      * @return string
313:      */
314:     public function escapeLike($value, $pos)
315:     {
316:         throw new DibiNotImplementedException;
317:     }
318: 
319: 
320: 
321:     /**
322:      * Decodes data from result set.
323:      * @param  string    value
324:      * @param  string    type (dibi::BINARY)
325:      * @return string    decoded value
326:      * @throws InvalidArgumentException
327:      */
328:     public function unescape($value, $type)
329:     {
330:         if ($type === dibi::BINARY) {
331:             return $value;
332:         }
333:         throw new InvalidArgumentException('Unsupported type.');
334:     }
335: 
336: 
337: 
338:     /**
339:      * Injects LIMIT/OFFSET to the SQL query.
340:      * @param  string &$sql  The SQL query that will be modified.
341:      * @param  int $limit
342:      * @param  int $offset
343:      * @return void
344:      */
345:     public function applyLimit(&$sql, $limit, $offset)
346:     {
347:         if ($limit < 0 && $offset < 1) return;
348: 
349:         switch ($this->driverName) {
350:         case 'mysql':
351:             $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
352:                 . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
353:             break;
354: 
355:         case 'pgsql':
356:             if ($limit >= 0) $sql .= ' LIMIT ' . (int) $limit;
357:             if ($offset > 0) $sql .= ' OFFSET ' . (int) $offset;
358:             break;
359: 
360:         case 'sqlite':
361:         case 'sqlite2':
362:             $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
363:             break;
364: 
365:         case 'oci':
366:             if ($offset > 0) {
367:                 $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ') WHERE "__rnum" > '. (int) $offset;
368:             } elseif ($limit >= 0) {
369:                 $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
370:             }
371:             break;
372: 
373:         case 'odbc':
374:         case 'mssql':
375:             if ($offset < 1) {
376:                 $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
377:                 break;
378:             }
379:             // intentionally break omitted
380: 
381:         default:
382:             throw new DibiNotSupportedException('PDO or driver does not support applying limit or offset.');
383:         }
384:     }
385: 
386: 
387: 
388:     /********************* result set ****************d*g**/
389: 
390: 
391: 
392:     /**
393:      * Returns the number of rows in a result set.
394:      * @return int
395:      */
396:     public function getRowCount()
397:     {
398:         return $this->resultSet->rowCount();
399:     }
400: 
401: 
402: 
403:     /**
404:      * Fetches the row at current position and moves the internal cursor to the next position.
405:      * @param  bool     TRUE for associative array, FALSE for numeric
406:      * @return array    array on success, nonarray if no next record
407:      */
408:     public function fetch($assoc)
409:     {
410:         return $this->resultSet->fetch($assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM);
411:     }
412: 
413: 
414: 
415:     /**
416:      * Moves cursor position without fetching row.
417:      * @param  int      the 0-based cursor pos to seek to
418:      * @return boolean  TRUE on success, FALSE if unable to seek to specified record
419:      */
420:     public function seek($row)
421:     {
422:         throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
423:     }
424: 
425: 
426: 
427:     /**
428:      * Frees the resources allocated for this result set.
429:      * @return void
430:      */
431:     public function free()
432:     {
433:         $this->resultSet = NULL;
434:     }
435: 
436: 
437: 
438:     /**
439:      * Returns metadata for all columns in a result set.
440:      * @return array
441:      * @throws DibiException
442:      */
443:     public function getResultColumns()
444:     {
445:         $count = $this->resultSet->columnCount();
446:         $columns = array();
447:         for ($i = 0; $i < $count; $i++) {
448:             $row = @$this->resultSet->getColumnMeta($i); // intentionally @
449:             if ($row === FALSE) {
450:                 throw new DibiNotSupportedException('Driver does not support meta data.');
451:             }
452:             // PHP < 5.2.3 compatibility
453:             // @see: http://php.net/manual/en/pdostatement.getcolumnmeta.php#pdostatement.getcolumnmeta.changelog
454:             $row['table'] = isset($row['table']) ? $row['table'] : NULL;
455: 
456:             $columns[] = array(
457:                 'name' => $row['name'],
458:                 'table' => $row['table'],
459:                 'nativetype' => $row['native_type'],
460:                 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
461:                 'vendor' => $row,
462:             );
463:         }
464:         return $columns;
465:     }
466: 
467: 
468: 
469:     /**
470:      * Returns the result set resource.
471:      * @return PDOStatement
472:      */
473:     public function getResultResource()
474:     {
475:         return $this->resultSet;
476:     }
477: 
478: }
479: 
dibi API documentation API documentation generated by ApiGen 2.3.0