PageRenderTime 433ms CodeModel.GetById 121ms app.highlight 219ms RepoModel.GetById 83ms app.codeStats 0ms

/www/libs/dibi/libs/DibiConnection.php

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