1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12:
13:
14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
27: class DibiConnection extends DibiObject
28: {
29:
30: public $onEvent;
31:
32:
33: private $config;
34:
35:
36: private $driver;
37:
38:
39: private $translator;
40:
41:
42: private $connected = FALSE;
43:
44:
45: private $substitutes;
46:
47:
48:
49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62:
63: public function __construct($config, $name = NULL)
64: {
65: class_exists('dibi');
66:
67:
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:
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: 145: 146:
147: public function __destruct()
148: {
149:
150: $this->connected && $this->driver->getResource() && $this->disconnect();
151: }
152:
153:
154:
155: 156: 157: 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: 177: 178:
179: final public function disconnect()
180: {
181: $this->driver->disconnect();
182: $this->connected = FALSE;
183: }
184:
185:
186:
187: 188: 189: 190:
191: final public function isConnected()
192: {
193: return $this->connected;
194: }
195:
196:
197:
198: 199: 200: 201: 202: 203: 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: 222: 223: 224: 225: 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: 242: 243:
244: final public function getDriver()
245: {
246: $this->connected || $this->connect();
247: return $this->driver;
248: }
249:
250:
251:
252: 253: 254: 255: 256: 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: 268: 269: 270: 271:
272: final public function translate($args)
273: {
274: $args = func_get_args();
275: return $this->translateArgs($args);
276: }
277:
278:
279:
280: 281: 282: 283: 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: 306: 307: 308: 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: 320: 321: 322:
323: private function translateArgs($args)
324: {
325: $this->connected || $this->connect();
326: return $this->translator->translate($args);
327: }
328:
329:
330:
331: 332: 333: 334: 335: 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: 364: 365: 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: 379: 380: 381:
382: public function affectedRows()
383: {
384: return $this->getAffectedRows();
385: }
386:
387:
388:
389: 390: 391: 392: 393: 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: 407: 408: 409: 410:
411: public function insertId($sequence = NULL)
412: {
413: return $this->getInsertId($sequence);
414: }
415:
416:
417:
418: 419: 420: 421: 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: 441: 442: 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: 462: 463: 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: 483: 484: 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:
496:
497:
498:
499: 500: 501:
502: public function command()
503: {
504: return new DibiFluent($this);
505: }
506:
507:
508:
509: 510: 511: 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: 523: 524: 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: 538: 539: 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: 556: 557:
558: public function delete($table)
559: {
560: return $this->command()->delete()->from('%n', $table);
561: }
562:
563:
564:
565:
566:
567:
568:
569: 570: 571: 572:
573: public function getSubstitutes()
574: {
575: return $this->substitutes;
576: }
577:
578:
579:
580: 581: 582: 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: 593:
594: private function subCb($m)
595: {
596: return $this->substitutes->{$m[1]};
597: }
598:
599:
600:
601:
602:
603:
604:
605: 606: 607: 608: 609: 610:
611: public function fetch($args)
612: {
613: $args = func_get_args();
614: return $this->query($args)->fetch();
615: }
616:
617:
618:
619: 620: 621: 622: 623: 624:
625: public function fetchAll($args)
626: {
627: $args = func_get_args();
628: return $this->query($args)->fetchAll();
629: }
630:
631:
632:
633: 634: 635: 636: 637: 638:
639: public function fetchSingle($args)
640: {
641: $args = func_get_args();
642: return $this->query($args)->fetchSingle();
643: }
644:
645:
646:
647: 648: 649: 650: 651: 652:
653: public function fetchPairs($args)
654: {
655: $args = func_get_args();
656: return $this->query($args)->fetchPairs();
657: }
658:
659:
660:
661:
662:
663:
664:
665: 666: 667: 668: 669:
670: public function loadFile($file)
671: {
672: $this->connected || $this->connect();
673: @set_time_limit(0);
674:
675: $handle = @fopen($file, 'r');
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: 699: 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: 711:
712: public function __wakeup()
713: {
714: throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
715: }
716:
717:
718:
719: 720: 721:
722: public function __sleep()
723: {
724: throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
725: }
726:
727: }
728: