Packages

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

Classes

  • dibi
  • DibiConnection
  • DibiDataSource
  • DibiDateTime
  • DibiEvent
  • DibiFileLogger
  • DibiFirePhpLogger
  • DibiFluent
  • DibiObject
  • DibiResult
  • DibiResultIterator
  • DibiRow
  • DibiTranslator

Interfaces

  • IDataSource
  • IDibiDriver
  • IDibiReflector
  • IDibiResultDriver

Exceptions

  • DibiDriverException
  • DibiException
  • DibiNotImplementedException
  • DibiNotSupportedException
  • DibiPcreException
  • 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: 
 14: /**
 15:  * dibi connection.
 16:  *
 17:  * @author     David Grudl
 18:  * @package    dibi
 19:  *
 20:  * @property-read bool $connected
 21:  * @property-read mixed $config
 22:  * @property-read IDibiDriver $driver
 23:  * @property-read int $affectedRows
 24:  * @property-read int $insertId
 25:  * @property-read DibiDatabaseInfo $databaseInfo
 26:  */
 27: class DibiConnection extends DibiObject
 28: {
 29:     /** @var array of function(DibiEvent $event); Occurs after query is executed */
 30:     public $onEvent;
 31: 
 32:     /** @var array  Current connection configuration */
 33:     private $config;
 34: 
 35:     /** @var IDibiDriver */
 36:     private $driver;
 37: 
 38:     /** @var DibiTranslator */
 39:     private $translator;
 40: 
 41:     /** @var bool  Is connected? */
 42:     private $connected = FALSE;
 43: 
 44:     /** @var DibiHashMap Substitutes for identifiers */
 45:     private $substitutes;
 46: 
 47: 
 48: 
 49:     /**
 50:      * Connection options: (see driver-specific options too)
 51:      *   - lazy (bool) => if TRUE, connection will be established only when required
 52:      *   - result (array) => result set options
 53:      *       - formatDateTime => date-time format (if empty, DateTime objects will be returned)
 54:      *   - profiler (array or bool)
 55:      *       - run (bool) => enable profiler?
 56:      *       - file => file to log
 57:      *   - substitutes (array) => map of driver specific substitutes (under development)
 58: 
 59:      * @param  mixed   connection parameters
 60:      * @param  string  connection name
 61:      * @throws DibiException
 62:      */
 63:     public function __construct($config, $name = NULL)
 64:     {
 65:         class_exists('dibi'); // ensure class dibi is loaded
 66: 
 67:         // DSN string
 68:         if (is_string($config)) {
 69:             parse_str($config, $config);
 70: 
 71:         } elseif ($config instanceof Traversable) {
 72:             $tmp = array();
 73:             foreach ($config as $key => $val) {
 74:                 $tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
 75:             }
 76:             $config = $tmp;
 77: 
 78:         } elseif (!is_array($config)) {
 79:             throw new InvalidArgumentException('Configuration must be array, string or object.');
 80:         }
 81: 
 82:         self::alias($config, 'username', 'user');
 83:         self::alias($config, 'password', 'pass');
 84:         self::alias($config, 'host', 'hostname');
 85:         self::alias($config, 'result|formatDate', 'resultDate');
 86:         self::alias($config, 'result|formatDateTime', 'resultDateTime');
 87: 
 88:         if (!isset($config['driver'])) {
 89:             $config['driver'] = dibi::$defaultDriver;
 90:         }
 91: 
 92:         $driver = preg_replace('#[^a-z0-9_]#', '_', strtolower($config['driver']));
 93:         $class = "Dibi" . $driver . "Driver";
 94:         if (!class_exists($class, FALSE)) {
 95:             include_once dirname(__FILE__) . "/../drivers/$driver.php";
 96: 
 97:             if (!class_exists($class, FALSE)) {
 98:                 throw new DibiException("Unable to create instance of dibi driver '$class'.");
 99:             }
100:         }
101: 
102:         $config['name'] = $name;
103:         $this->config = $config;
104:         $this->driver = new $class;
105:         $this->translator = new DibiTranslator($this);
106: 
107:         // profiler
108:         $profilerCfg = & $config['profiler'];
109:         if (is_scalar($profilerCfg)) {
110:             $profilerCfg = array('run' => (bool) $profilerCfg);
111:         }
112:         if (!empty($profilerCfg['run'])) {
113:             $filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : DibiEvent::QUERY;
114: 
115:             if (isset($profilerCfg['file'])) {
116:                 $this->onEvent[] = array(new DibiFileLogger($profilerCfg['file'], $filter), 'logEvent');
117:             }
118: 
119:             if (DibiFirePhpLogger::isAvailable()) {
120:                 $this->onEvent[] = array(new DibiFirePhpLogger($filter), 'logEvent');
121:             }
122: 
123:             if (class_exists('DibiNettePanel', FALSE)) {
124:                 $panel = new DibiNettePanel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
125:                 $panel->register($this);
126:             }
127:         }
128: 
129:         $this->substitutes = new DibiHashMap(create_function('$expr', 'return ":$expr:";'));
130:         if (!empty($config['substitutes'])) {
131:             foreach ($config['substitutes'] as $key => $value) {
132:                 $this->substitutes->$key = $value;
133:             }
134:         }
135: 
136:         if (empty($config['lazy'])) {
137:             $this->connect();
138:         }
139:     }
140: 
141: 
142: 
143:     /**
144:      * Automatically frees the resources allocated for this result set.
145:      * @return void
146:      */
147:     public function __destruct()
148:     {
149:         // disconnects and rolls back transaction - do not rely on auto-disconnect and rollback!
150:         $this->connected && $this->driver->getResource() && $this->disconnect();
151:     }
152: 
153: 
154: 
155:     /**
156:      * Connects to a database.
157:      * @return void
158:      */
159:     final public function connect()
160:     {
161:         $event = $this->onEvent ? new DibiEvent($this, DibiEvent::CONNECT) : NULL;
162:         try {
163:             $this->driver->connect($this->config);
164:             $this->connected = TRUE;
165:             $event && $this->onEvent($event->done());
166: 
167:         } catch (DibiException $e) {
168:             $event && $this->onEvent($event->done($e));
169:             throw $e;
170:         }
171:     }
172: 
173: 
174: 
175:     /**
176:      * Disconnects from a database.
177:      * @return void
178:      */
179:     final public function disconnect()
180:     {
181:         $this->driver->disconnect();
182:         $this->connected = FALSE;
183:     }
184: 
185: 
186: 
187:     /**
188:      * Returns TRUE when connection was established.
189:      * @return bool
190:      */
191:     final public function isConnected()
192:     {
193:         return $this->connected;
194:     }
195: 
196: 
197: 
198:     /**
199:      * Returns configuration variable. If no $key is passed, returns the entire array.
200:      * @see self::__construct
201:      * @param  string
202:      * @param  mixed  default value to use if key not found
203:      * @return mixed
204:      */
205:     final public function getConfig($key = NULL, $default = NULL)
206:     {
207:         if ($key === NULL) {
208:             return $this->config;
209: 
210:         } elseif (isset($this->config[$key])) {
211:             return $this->config[$key];
212: 
213:         } else {
214:             return $default;
215:         }
216:     }
217: 
218: 
219: 
220:     /**
221:      * Apply configuration alias or default values.
222:      * @param  array  connect configuration
223:      * @param  string key
224:      * @param  string alias key
225:      * @return void
226:      */
227:     public static function alias(&$config, $key, $alias)
228:     {
229:         $foo = & $config;
230:         foreach (explode('|', $key) as $key) $foo = & $foo[$key];
231: 
232:         if (!isset($foo) && isset($config[$alias])) {
233:             $foo = $config[$alias];
234:             unset($config[$alias]);
235:         }
236:     }
237: 
238: 
239: 
240:     /**
241:      * Returns the driver and connects to a database in lazy mode.
242:      * @return IDibiDriver
243:      */
244:     final public function getDriver()
245:     {
246:         $this->connected || $this->connect();
247:         return $this->driver;
248:     }
249: 
250: 
251: 
252:     /**
253:      * Generates (translates) and executes SQL query.
254:      * @param  array|mixed      one or more arguments
255:      * @return DibiResult|int   result set object (if any)
256:      * @throws DibiException
257:      */
258:     final public function query($args)
259:     {
260:         $args = func_get_args();
261:         return $this->nativeQuery($this->translateArgs($args));
262:     }
263: 
264: 
265: 
266:     /**
267:      * Generates SQL query.
268:      * @param  array|mixed      one or more arguments
269:      * @return string
270:      * @throws DibiException
271:      */
272:     final public function translate($args)
273:     {
274:         $args = func_get_args();
275:         return $this->translateArgs($args);
276:     }
277: 
278: 
279: 
280:     /**
281:      * Generates and prints SQL query.
282:      * @param  array|mixed  one or more arguments
283:      * @return bool
284:      */
285:     final public function test($args)
286:     {
287:         $args = func_get_args();
288:         try {
289:             dibi::dump($this->translateArgs($args));
290:             return TRUE;
291: 
292:         } catch (DibiException $e) {
293:             if ($e->getSql()) {
294:                 dibi::dump($e->getSql());
295:             } else {
296:                 echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
297:             }
298:             return FALSE;
299:         }
300:     }
301: 
302: 
303: 
304:     /**
305:      * Generates (translates) and returns SQL query as DibiDataSource.
306:      * @param  array|mixed      one or more arguments
307:      * @return DibiDataSource
308:      * @throws DibiException
309:      */
310:     final public function dataSource($args)
311:     {
312:         $args = func_get_args();
313:         return new DibiDataSource($this->translateArgs($args), $this);
314:     }
315: 
316: 
317: 
318:     /**
319:      * Generates SQL query.
320:      * @param  array
321:      * @return string
322:      */
323:     private function translateArgs($args)
324:     {
325:         $this->connected || $this->connect();
326:         return $this->translator->translate($args);
327:     }
328: 
329: 
330: 
331:     /**
332:      * Executes the SQL query.
333:      * @param  string           SQL statement.
334:      * @return DibiResult|int   result set object (if any)
335:      * @throws DibiException
336:      */
337:     final public function nativeQuery($sql)
338:     {
339:         $this->connected || $this->connect();
340: 
341:         $event = $this->onEvent ? new DibiEvent($this, DibiEvent::QUERY, $sql) : NULL;
342:         try {
343:             $res = $this->driver->query($sql);
344: 
345:         } catch (DibiException $e) {
346:             $event && $this->onEvent($event->done($e));
347:             throw $e;
348:         }
349: 
350:         if ($res) {
351:             $res = $this->createResultSet($res);
352:         } else {
353:             $res = $this->driver->getAffectedRows();
354:         }
355: 
356:         $event && $this->onEvent($event->done($res));
357:         return $res;
358:     }
359: 
360: 
361: 
362:     /**
363:      * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
364:      * @return int  number of rows
365:      * @throws DibiException
366:      */
367:     public function getAffectedRows()
368:     {
369:         $this->connected || $this->connect();
370:         $rows = $this->driver->getAffectedRows();
371:         if (!is_int($rows) || $rows < 0) throw new DibiException('Cannot retrieve number of affected rows.');
372:         return $rows;
373:     }
374: 
375: 
376: 
377:     /**
378:      * Gets the number of affected rows. Alias for getAffectedRows().
379:      * @return int  number of rows
380:      * @throws DibiException
381:      */
382:     public function affectedRows()
383:     {
384:         return $this->getAffectedRows();
385:     }
386: 
387: 
388: 
389:     /**
390:      * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
391:      * @param  string     optional sequence name
392:      * @return int
393:      * @throws DibiException
394:      */
395:     public function getInsertId($sequence = NULL)
396:     {
397:         $this->connected || $this->connect();
398:         $id = $this->driver->getInsertId($sequence);
399:         if ($id < 1) throw new DibiException('Cannot retrieve last generated ID.');
400:         return (int) $id;
401:     }
402: 
403: 
404: 
405:     /**
406:      * Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
407:      * @param  string     optional sequence name
408:      * @return int
409:      * @throws DibiException
410:      */
411:     public function insertId($sequence = NULL)
412:     {
413:         return $this->getInsertId($sequence);
414:     }
415: 
416: 
417: 
418:     /**
419:      * Begins a transaction (if supported).
420:      * @param  string  optional savepoint name
421:      * @return void
422:      */
423:     public function begin($savepoint = NULL)
424:     {
425:         $this->connected || $this->connect();
426:         $event = $this->onEvent ? new DibiEvent($this, DibiEvent::BEGIN, $savepoint) : NULL;
427:         try {
428:             $this->driver->begin($savepoint);
429:             $event && $this->onEvent($event->done());
430: 
431:         } catch (DibiException $e) {
432:             $event && $this->onEvent($event->done($e));
433:             throw $e;
434:         }
435:     }
436: 
437: 
438: 
439:     /**
440:      * Commits statements in a transaction.
441:      * @param  string  optional savepoint name
442:      * @return void
443:      */
444:     public function commit($savepoint = NULL)
445:     {
446:         $this->connected || $this->connect();
447:         $event = $this->onEvent ? new DibiEvent($this, DibiEvent::COMMIT, $savepoint) : NULL;
448:         try {
449:             $this->driver->commit($savepoint);
450:             $event && $this->onEvent($event->done());
451: 
452:         } catch (DibiException $e) {
453:             $event && $this->onEvent($event->done($e));
454:             throw $e;
455:         }
456:     }
457: 
458: 
459: 
460:     /**
461:      * Rollback changes in a transaction.
462:      * @param  string  optional savepoint name
463:      * @return void
464:      */
465:     public function rollback($savepoint = NULL)
466:     {
467:         $this->connected || $this->connect();
468:         $event = $this->onEvent ? new DibiEvent($this, DibiEvent::ROLLBACK, $savepoint) : NULL;
469:         try {
470:             $this->driver->rollback($savepoint);
471:             $event && $this->onEvent($event->done());
472: 
473:         } catch (DibiException $e) {
474:             $event && $this->onEvent($event->done($e));
475:             throw $e;
476:         }
477:     }
478: 
479: 
480: 
481:     /**
482:      * Result set factory.
483:      * @param  IDibiResultDriver
484:      * @return DibiResult
485:      */
486:     public function createResultSet(IDibiResultDriver $resultDriver)
487:     {
488:         $res = new DibiResult($resultDriver);
489:         return $res->setFormat(dibi::DATE, $this->config['result']['formatDate'])
490:             ->setFormat(dibi::DATETIME, $this->config['result']['formatDateTime']);
491:     }
492: 
493: 
494: 
495:     /********************* fluent SQL builders ****************d*g**/
496: 
497: 
498: 
499:     /**
500:      * @return DibiFluent
501:      */
502:     public function command()
503:     {
504:         return new DibiFluent($this);
505:     }
506: 
507: 
508: 
509:     /**
510:      * @param  string    column name
511:      * @return DibiFluent
512:      */
513:     public function select($args)
514:     {
515:         $args = func_get_args();
516:         return $this->command()->__call('select', $args);
517:     }
518: 
519: 
520: 
521:     /**
522:      * @param  string   table
523:      * @param  array
524:      * @return DibiFluent
525:      */
526:     public function update($table, $args)
527:     {
528:         if (!(is_array($args) || $args instanceof Traversable)) {
529:             throw new InvalidArgumentException('Arguments must be array or Traversable.');
530:         }
531:         return $this->command()->update('%n', $table)->set($args);
532:     }
533: 
534: 
535: 
536:     /**
537:      * @param  string   table
538:      * @param  array
539:      * @return DibiFluent
540:      */
541:     public function insert($table, $args)
542:     {
543:         if ($args instanceof Traversable) {
544:             $args = iterator_to_array($args);
545:         } elseif (!is_array($args)) {
546:             throw new InvalidArgumentException('Arguments must be array or Traversable.');
547:         }
548:         return $this->command()->insert()
549:             ->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
550:     }
551: 
552: 
553: 
554:     /**
555:      * @param  string   table
556:      * @return DibiFluent
557:      */
558:     public function delete($table)
559:     {
560:         return $this->command()->delete()->from('%n', $table);
561:     }
562: 
563: 
564: 
565:     /********************* substitutions ****************d*g**/
566: 
567: 
568: 
569:     /**
570:      * Returns substitution hashmap.
571:      * @return DibiHashMap
572:      */
573:     public function getSubstitutes()
574:     {
575:         return $this->substitutes;
576:     }
577: 
578: 
579: 
580:     /**
581:      * Provides substitution.
582:      * @return string
583:      */
584:     public function substitute($value)
585:     {
586:         return strpos($value, ':') === FALSE ? $value : preg_replace_callback('#:([^:\s]*):#', array($this, 'subCb'), $value);
587:     }
588: 
589: 
590: 
591:     /**
592:      * Substitution callback.
593:      */
594:     private function subCb($m)
595:     {
596:         return $this->substitutes->{$m[1]};
597:     }
598: 
599: 
600: 
601:     /********************* shortcuts ****************d*g**/
602: 
603: 
604: 
605:     /**
606:      * Executes SQL query and fetch result - shortcut for query() & fetch().
607:      * @param  array|mixed    one or more arguments
608:      * @return DibiRow
609:      * @throws DibiException
610:      */
611:     public function fetch($args)
612:     {
613:         $args = func_get_args();
614:         return $this->query($args)->fetch();
615:     }
616: 
617: 
618: 
619:     /**
620:      * Executes SQL query and fetch results - shortcut for query() & fetchAll().
621:      * @param  array|mixed    one or more arguments
622:      * @return array of DibiRow
623:      * @throws DibiException
624:      */
625:     public function fetchAll($args)
626:     {
627:         $args = func_get_args();
628:         return $this->query($args)->fetchAll();
629:     }
630: 
631: 
632: 
633:     /**
634:      * Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
635:      * @param  array|mixed    one or more arguments
636:      * @return string
637:      * @throws DibiException
638:      */
639:     public function fetchSingle($args)
640:     {
641:         $args = func_get_args();
642:         return $this->query($args)->fetchSingle();
643:     }
644: 
645: 
646: 
647:     /**
648:      * Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
649:      * @param  array|mixed    one or more arguments
650:      * @return string
651:      * @throws DibiException
652:      */
653:     public function fetchPairs($args)
654:     {
655:         $args = func_get_args();
656:         return $this->query($args)->fetchPairs();
657:     }
658: 
659: 
660: 
661:     /********************* misc ****************d*g**/
662: 
663: 
664: 
665:     /**
666:      * Import SQL dump from file - extreme fast!
667:      * @param  string  filename
668:      * @return int  count of sql commands
669:      */
670:     public function loadFile($file)
671:     {
672:         $this->connected || $this->connect();
673:         @set_time_limit(0); // intentionally @
674: 
675:         $handle = @fopen($file, 'r'); // intentionally @
676:         if (!$handle) {
677:             throw new RuntimeException("Cannot open file '$file'.");
678:         }
679: 
680:         $count = 0;
681:         $sql = '';
682:         while (!feof($handle)) {
683:             $s = fgets($handle);
684:             $sql .= $s;
685:             if (substr(rtrim($s), -1) === ';') {
686:                 $this->driver->query($sql);
687:                 $sql = '';
688:                 $count++;
689:             }
690:         }
691:         fclose($handle);
692:         return $count;
693:     }
694: 
695: 
696: 
697:     /**
698:      * Gets a information about the current database.
699:      * @return DibiDatabaseInfo
700:      */
701:     public function getDatabaseInfo()
702:     {
703:         $this->connected || $this->connect();
704:         return new DibiDatabaseInfo($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
705:     }
706: 
707: 
708: 
709:     /**
710:      * Prevents unserialization.
711:      */
712:     public function __wakeup()
713:     {
714:         throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
715:     }
716: 
717: 
718: 
719:     /**
720:      * Prevents serialization.
721:      */
722:     public function __sleep()
723:     {
724:         throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
725:     }
726: 
727: }
728: 
Dibi 2.0.1 API documentation API documentation generated by ApiGen 2.3.0