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