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:
28: class DibiConnection extends DibiObject
29: {
30:
31: private $config;
32:
33:
34: private $driver;
35:
36:
37: private $translator;
38:
39:
40: private $profiler;
41:
42:
43: private $connected = FALSE;
44:
45:
46:
47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61:
62: public function __construct($config, $name = NULL)
63: {
64: 65: if (is_string($config)) {
66: parse_str($config, $config);
67:
68: } elseif ($config instanceof Traversable) {
69: $tmp = array();
70: foreach ($config as $key => $val) {
71: $tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
72: }
73: $config = $tmp;
74:
75: } elseif (!is_array($config)) {
76: throw new InvalidArgumentException('Configuration must be array, string or object.');
77: }
78:
79: self::alias($config, 'username', 'user');
80: self::alias($config, 'password', 'pass');
81: self::alias($config, 'host', 'hostname');
82: self::alias($config, 'result|detectTypes', 'resultDetectTypes'); 83: self::alias($config, 'result|formatDateTime', 'resultDateTime');
84:
85: if (!isset($config['driver'])) {
86: $config['driver'] = dibi::$defaultDriver;
87: }
88:
89: $driver = preg_replace('#[^a-z0-9_]#', '_', strtolower($config['driver']));
90: $class = "Dibi" . $driver . "Driver";
91: if (!class_exists($class, FALSE)) {
92: include_once dirname(__FILE__) . "/../drivers/$driver.php";
93:
94: if (!class_exists($class, FALSE)) {
95: throw new DibiException("Unable to create instance of dibi driver '$class'.");
96: }
97: }
98:
99: $config['name'] = $name;
100: $this->config = $config;
101: $this->driver = new $class;
102: $this->translator = new DibiTranslator($this->driver);
103:
104: 105: $profilerCfg = & $config['profiler'];
106: if (is_scalar($profilerCfg)) { 107: $profilerCfg = array(
108: 'run' => (bool) $profilerCfg,
109: 'class' => strlen($profilerCfg) > 1 ? $profilerCfg : NULL,
110: );
111: }
112:
113: if (!empty($profilerCfg['run'])) {
114: $class = isset($profilerCfg['class']) ? $profilerCfg['class'] : 'DibiProfiler';
115: if (!class_exists($class)) {
116: throw new DibiException("Unable to create instance of dibi profiler '$class'.");
117: }
118: $this->setProfiler(new $class($profilerCfg));
119: }
120:
121: if (!empty($config['substitutes'])) {
122: foreach ($config['substitutes'] as $key => $value) {
123: dibi::addSubst($key, $value);
124: }
125: }
126:
127: if (empty($config['lazy'])) {
128: $this->connect();
129: }
130: }
131:
132:
133:
134: 135: 136: 137:
138: public function __destruct()
139: {
140: 141: $this->connected && $this->disconnect();
142: }
143:
144:
145:
146: 147: 148: 149:
150: final public function connect()
151: {
152: if ($this->profiler !== NULL) {
153: $ticket = $this->profiler->before($this, IDibiProfiler::CONNECT);
154: }
155: $this->driver->connect($this->config);
156: $this->connected = TRUE;
157: if (isset($ticket)) {
158: $this->profiler->after($ticket);
159: }
160: }
161:
162:
163:
164: 165: 166: 167:
168: final public function disconnect()
169: {
170: $this->driver->disconnect();
171: $this->connected = FALSE;
172: }
173:
174:
175:
176: 177: 178: 179:
180: final public function isConnected()
181: {
182: return $this->connected;
183: }
184:
185:
186:
187: 188: 189: 190: 191: 192: 193:
194: final public function getConfig($key = NULL, $default = NULL)
195: {
196: if ($key === NULL) {
197: return $this->config;
198:
199: } elseif (isset($this->config[$key])) {
200: return $this->config[$key];
201:
202: } else {
203: return $default;
204: }
205: }
206:
207:
208:
209: 210: 211: 212: 213: 214: 215:
216: public static function alias(&$config, $key, $alias)
217: {
218: $foo = & $config;
219: foreach (explode('|', $key) as $key) $foo = & $foo[$key];
220:
221: if (!isset($foo) && isset($config[$alias])) {
222: $foo = $config[$alias];
223: unset($config[$alias]);
224: }
225: }
226:
227:
228:
229: 230: 231: 232:
233: final public function getDriver()
234: {
235: $this->connected || $this->connect();
236: return $this->driver;
237: }
238:
239:
240:
241: 242: 243: 244: 245: 246:
247: final public function query($args)
248: {
249: $this->connected || $this->connect();
250: $args = func_get_args();
251: return $this->nativeQuery($this->translator->translate($args));
252: }
253:
254:
255:
256: 257: 258: 259: 260: 261:
262: final public function translate($args)
263: {
264: $this->connected || $this->connect();
265: $args = func_get_args();
266: return $this->translator->translate($args);
267: }
268:
269:
270:
271:
272: function sql($args)
273: {
274: trigger_error(__METHOD__ . '() is deprecated; use translate() instead.', E_USER_NOTICE);
275: $this->connected || $this->connect();
276: $args = func_get_args();
277: return $this->translator->translate($args);
278: }
279:
280:
281:
282: 283: 284: 285: 286:
287: final public function test($args)
288: {
289: $this->connected || $this->connect();
290: $args = func_get_args();
291: try {
292: dibi::dump($this->translator->translate($args));
293: return TRUE;
294:
295: } catch (DibiException $e) {
296: dibi::dump($e->getSql());
297: return FALSE;
298: }
299: }
300:
301:
302:
303: 304: 305: 306: 307: 308:
309: final public function dataSource($args)
310: {
311: $this->connected || $this->connect();
312: $args = func_get_args();
313: return new DibiDataSource($this->translator->translate($args), $this);
314: }
315:
316:
317:
318: 319: 320: 321: 322: 323:
324: final public function nativeQuery($sql)
325: {
326: $this->connected || $this->connect();
327:
328: if ($this->profiler !== NULL) {
329: $event = IDibiProfiler::QUERY;
330: if (preg_match('#\s*(SELECT|UPDATE|INSERT|DELETE)#i', $sql, $matches)) {
331: static $events = array(
332: 'SELECT' => IDibiProfiler::SELECT, 'UPDATE' => IDibiProfiler::UPDATE,
333: 'INSERT' => IDibiProfiler::INSERT, 'DELETE' => IDibiProfiler::DELETE,
334: );
335: $event = $events[strtoupper($matches[1])];
336: }
337: $ticket = $this->profiler->before($this, $event, $sql);
338: }
339:
340: dibi::$sql = $sql;
341: if ($res = $this->driver->query($sql)) { 342: $res = new DibiResult($res, $this->config['result']);
343: } else {
344: $res = $this->driver->getAffectedRows();
345: }
346:
347: if (isset($ticket)) {
348: $this->profiler->after($ticket, $res);
349: }
350: return $res;
351: }
352:
353:
354:
355: 356: 357: 358: 359:
360: public function getAffectedRows()
361: {
362: $this->connected || $this->connect();
363: $rows = $this->driver->getAffectedRows();
364: if (!is_int($rows) || $rows < 0) throw new DibiException('Cannot retrieve number of affected rows.');
365: return $rows;
366: }
367:
368:
369:
370: 371: 372: 373: 374:
375: public function affectedRows()
376: {
377: return $this->getAffectedRows();
378: }
379:
380:
381:
382: 383: 384: 385: 386: 387:
388: public function getInsertId($sequence = NULL)
389: {
390: $this->connected || $this->connect();
391: $id = $this->driver->getInsertId($sequence);
392: if ($id < 1) throw new DibiException('Cannot retrieve last generated ID.');
393: return (int) $id;
394: }
395:
396:
397:
398: 399: 400: 401: 402: 403:
404: public function insertId($sequence = NULL)
405: {
406: return $this->getInsertId($sequence);
407: }
408:
409:
410:
411: 412: 413: 414: 415:
416: public function begin($savepoint = NULL)
417: {
418: $this->connected || $this->connect();
419: if ($this->profiler !== NULL) {
420: $ticket = $this->profiler->before($this, IDibiProfiler::BEGIN, $savepoint);
421: }
422: $this->driver->begin($savepoint);
423: if (isset($ticket)) {
424: $this->profiler->after($ticket);
425: }
426: }
427:
428:
429:
430: 431: 432: 433: 434:
435: public function commit($savepoint = NULL)
436: {
437: $this->connected || $this->connect();
438: if ($this->profiler !== NULL) {
439: $ticket = $this->profiler->before($this, IDibiProfiler::COMMIT, $savepoint);
440: }
441: $this->driver->commit($savepoint);
442: if (isset($ticket)) {
443: $this->profiler->after($ticket);
444: }
445: }
446:
447:
448:
449: 450: 451: 452: 453:
454: public function rollback($savepoint = NULL)
455: {
456: $this->connected || $this->connect();
457: if ($this->profiler !== NULL) {
458: $ticket = $this->profiler->before($this, IDibiProfiler::ROLLBACK, $savepoint);
459: }
460: $this->driver->rollback($savepoint);
461: if (isset($ticket)) {
462: $this->profiler->after($ticket);
463: }
464: }
465:
466:
467:
468:
469:
470:
471:
472: 473: 474:
475: public function command()
476: {
477: return new DibiFluent($this);
478: }
479:
480:
481:
482: 483: 484: 485:
486: public function select($args)
487: {
488: $args = func_get_args();
489: return $this->command()->__call('select', $args);
490: }
491:
492:
493:
494: 495: 496: 497: 498:
499: public function update($table, $args)
500: {
501: if (!(is_array($args) || $args instanceof Traversable)) {
502: throw new InvalidArgumentException('Arguments must be array or Traversable.');
503: }
504: return $this->command()->update('%n', $table)->set($args);
505: }
506:
507:
508:
509: 510: 511: 512: 513:
514: public function insert($table, $args)
515: {
516: if ($args instanceof Traversable) {
517: $args = iterator_to_array($args);
518: } elseif (!is_array($args)) {
519: throw new InvalidArgumentException('Arguments must be array or Traversable.');
520: }
521: return $this->command()->insert()
522: ->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
523: }
524:
525:
526:
527: 528: 529: 530:
531: public function delete($table)
532: {
533: return $this->command()->delete()->from('%n', $table);
534: }
535:
536:
537:
538:
539:
540:
541:
542: 543: 544: 545:
546: public function setProfiler(IDibiProfiler $profiler = NULL)
547: {
548: $this->profiler = $profiler;
549: return $this;
550: }
551:
552:
553:
554: 555: 556:
557: public function getProfiler()
558: {
559: return $this->profiler;
560: }
561:
562:
563:
564:
565:
566:
567:
568: 569: 570: 571: 572: 573:
574: public function fetch($args)
575: {
576: $args = func_get_args();
577: return $this->query($args)->fetch();
578: }
579:
580:
581:
582: 583: 584: 585: 586: 587:
588: public function fetchAll($args)
589: {
590: $args = func_get_args();
591: return $this->query($args)->fetchAll();
592: }
593:
594:
595:
596: 597: 598: 599: 600: 601:
602: public function fetchSingle($args)
603: {
604: $args = func_get_args();
605: return $this->query($args)->fetchSingle();
606: }
607:
608:
609:
610: 611: 612: 613: 614: 615:
616: public function fetchPairs($args)
617: {
618: $args = func_get_args();
619: return $this->query($args)->fetchPairs();
620: }
621:
622:
623:
624:
625:
626:
627:
628: 629: 630: 631: 632:
633: public function loadFile($file)
634: {
635: $this->connected || $this->connect();
636: @set_time_limit(0); 637:
638: $handle = @fopen($file, 'r'); 639: if (!$handle) {
640: throw new FileNotFoundException("Cannot open file '$file'.");
641: }
642:
643: $count = 0;
644: $sql = '';
645: while (!feof($handle)) {
646: $s = fgets($handle);
647: $sql .= $s;
648: if (substr(rtrim($s), -1) === ';') {
649: $this->driver->query($sql);
650: $sql = '';
651: $count++;
652: }
653: }
654: fclose($handle);
655: return $count;
656: }
657:
658:
659:
660: 661: 662: 663:
664: public function getDatabaseInfo()
665: {
666: $this->connected || $this->connect();
667: return new DibiDatabaseInfo($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
668: }
669:
670:
671:
672: 673: 674:
675: public function __wakeup()
676: {
677: throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
678: }
679:
680:
681:
682: 683: 684:
685: public function __sleep()
686: {
687: throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
688: }
689:
690: }
691: