PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Atomik/Db/Instance.php

http://atomikframework.googlecode.com/
PHP | 638 lines | 306 code | 69 blank | 263 comment | 37 complexity | 51b61a70a97a467fec1ec92b04b551ba MD5 | raw file
Possible License(s): LGPL-2.1, MIT, CC-BY-3.0
  1. <?php
  2. /**
  3. * Atomik Framework
  4. * Copyright (c) 2008-2009 Maxime Bouroumeau-Fuseau
  5. *
  6. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  7. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  8. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  9. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  10. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  11. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  12. * THE SOFTWARE.
  13. *
  14. * @package Atomik
  15. * @subpackage Db
  16. * @author Maxime Bouroumeau-Fuseau
  17. * @copyright 2008-2009 (c) Maxime Bouroumeau-Fuseau
  18. * @license http://www.opensource.org/licenses/mit-license.php
  19. * @link http://www.atomikframework.com
  20. */
  21. /** Atomik_Db_Query */
  22. require_once 'Atomik/Db/Query.php';
  23. /** Atomik_Db_Adapter */
  24. require_once 'Atomik/Db/Adapter.php';
  25. /** Atomik_Db_Adapter_Interface */
  26. require_once 'Atomik/Db/Adapter/Interface.php';
  27. /**
  28. * Helpers function for handling databases
  29. *
  30. * @package Atomik
  31. * @subpackage Db
  32. */
  33. class Atomik_Db_Instance
  34. {
  35. /** @var PDO */
  36. public $pdo;
  37. /** @var array */
  38. public $connectionInfo;
  39. /** @var Atomik_Db_Adapter_Interface */
  40. protected $_adapter;
  41. /** @var string */
  42. protected $_tablePrefix = '';
  43. /** @var array */
  44. protected $_errorInfo;
  45. /** @var bool */
  46. protected $_inTransaction = false;
  47. /** @var string */
  48. protected static $_defaultTablePrefix = '';
  49. /**
  50. * Sets the prefix that will be appended to all table names
  51. *
  52. * @param string $prefix
  53. */
  54. public static function setDefaultTablePrefix($prefix)
  55. {
  56. if (empty($prefix)) {
  57. $prefix = '';
  58. }
  59. self::$_defaultTablePrefix = $prefix;
  60. }
  61. /**
  62. * Returns the table prefix
  63. *
  64. * @return string
  65. */
  66. public static function getDefaultTablePrefix()
  67. {
  68. return self::$_defaultTablePrefix;
  69. }
  70. /**
  71. * Constructor
  72. *
  73. * @param PDO $pdo
  74. */
  75. public function __construct($dsnOrPdo = null, $username = '', $password = '')
  76. {
  77. $this->_tablePrefix = self::getDefaultTablePrefix();
  78. if ($dsnOrPdo instanceof PDO) {
  79. $this->pdo = $pdo;
  80. return;
  81. }
  82. $this->setConnectionInfo($dsnOrPdo, $username, $password);
  83. }
  84. /**
  85. * Sets the connection information
  86. *
  87. * @param string $dsn
  88. * @param string $username
  89. * @param string $password
  90. */
  91. public function setConnectionInfo($dsn, $username, $password = '')
  92. {
  93. if ($this->pdo !== null) {
  94. require_once 'Atomik/Db/Instance/Exception.php';
  95. throw new Atomik_Db_Instance_Exception('Connection information cannot be set after the connection '
  96. . 'have been established, you must disconnect first');
  97. }
  98. $this->connectionInfo = array(
  99. 'dsn' => $dsn,
  100. 'username' => $username,
  101. 'password' => $password
  102. );
  103. }
  104. /**
  105. * Returns connection information
  106. *
  107. * @return array
  108. */
  109. public function getConnectionInfo()
  110. {
  111. return $this->connectionInfo;
  112. }
  113. /**
  114. * Connects to the database
  115. *
  116. * If no params are provided, connection information
  117. * will be used, {@see setConnectionInfo()}
  118. *
  119. * @param string $dsn
  120. * @param string $username
  121. * @param string $password
  122. */
  123. public function connect($dsn = null, $username = '', $password = '')
  124. {
  125. if ($this->pdo !== null) {
  126. // already connected
  127. return;
  128. }
  129. if ($dsn !== null) {
  130. $this->setConnectionInfo($dsn, $username, $password);
  131. }
  132. $info = $this->getConnectionInfo();
  133. // creates the pdo instance
  134. try {
  135. $this->pdo = new PDO($info['dsn'], $info['username'], $info['password']);
  136. } catch (Exception $e) {
  137. require_once 'Atomik/Db/Exception.php';
  138. throw new Atomik_Db_Exception('Database connection failed: ' . $e->getMessage());
  139. }
  140. }
  141. /**
  142. * Closes the database connection
  143. */
  144. public function disconnect()
  145. {
  146. $this->pdo = null;
  147. $this->_pdoDriver = null;
  148. $this->emptyCache();
  149. }
  150. /**
  151. * Returns the managed PDO object
  152. *
  153. * @return PDO
  154. */
  155. public function getPdo()
  156. {
  157. $this->connect();
  158. return $this->pdo;
  159. }
  160. /**
  161. * Returns the PDO driver being used
  162. *
  163. * @return string
  164. */
  165. public function getPdoDriverName()
  166. {
  167. $this->connect();
  168. return $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
  169. }
  170. /**
  171. * Returns the adapter associated to this instance
  172. *
  173. * @return Atomik_Db_Adapter_Interface
  174. */
  175. public function getAdapter()
  176. {
  177. if ($this->_adapter === null) {
  178. $this->connect();
  179. $this->_adapter = Atomik_Db_Adapter::factory($this->getPdoDriverName(), $this->pdo);
  180. }
  181. return $this->_adapter;
  182. }
  183. /**
  184. * Returns a quoted value
  185. *
  186. * @param string $text
  187. * @return string
  188. */
  189. public function quote($text)
  190. {
  191. return $this->getAdapter()->quote($text);
  192. }
  193. /**
  194. * Returns a quoted identifier
  195. *
  196. * @param string $id
  197. * @return string
  198. */
  199. public function quoteIdentifier($id)
  200. {
  201. return $this->getAdapter()->quoteIdentifier($id);
  202. }
  203. /**
  204. * Sets the prefix that will be prepended to all table names
  205. *
  206. * @param string $prefix
  207. */
  208. public function setTablePrefix($prefix)
  209. {
  210. $this->_tablePrefix = $prefix;
  211. }
  212. /**
  213. * Returns the table prefix
  214. *
  215. * @return string
  216. */
  217. public function getTablePrefix()
  218. {
  219. return $this->_tablePrefix;
  220. }
  221. /**
  222. * Returns error information
  223. *
  224. * @param int $index
  225. * @return array
  226. */
  227. public function getErrorInfo($index = null)
  228. {
  229. if ($index !== null) {
  230. return $this->_errorInfo[$index];
  231. }
  232. return $this->_errorInfo;
  233. }
  234. /**
  235. * Creates a query associated to this instance
  236. *
  237. * @return Atomik_Db_Query
  238. */
  239. public function q()
  240. {
  241. $this->connect();
  242. return new Atomik_Db_Query($this);
  243. }
  244. /**
  245. * Prepares and executes a statement
  246. *
  247. * @param string $query
  248. * @param array $params
  249. * @return PDOStatement
  250. */
  251. public function query($query, $params = array())
  252. {
  253. $this->connect();
  254. if (($stmt = $this->pdo->prepare((string) $query)) === false) {
  255. $this->_errorInfo = $this->pdo->errorInfo();
  256. return false;
  257. }
  258. if (!$stmt->execute((array) $params)) {
  259. $this->_errorInfo = $stmt->errorInfo();
  260. return false;
  261. }
  262. return $stmt;
  263. }
  264. /**
  265. * Executes a statement
  266. *
  267. * If $params is not empty, will use {@see query()}
  268. *
  269. * @see PDO::exec()
  270. * @param string $sql
  271. * @param array $params
  272. * @return int Number of affected rows
  273. */
  274. public function exec($sql, $params = null)
  275. {
  276. $this->connect();
  277. if (!empty($params)) {
  278. return $this->query($sql, $params);
  279. }
  280. if (($nbRowAffected = $this->pdo->exec((string) $sql)) === false) {
  281. $this->_errorInfo = $this->pdo->errorInfo();
  282. return false;
  283. }
  284. return $nbRowAffected;
  285. }
  286. /**
  287. * Prepares a statement
  288. *
  289. * @see PDO::prepare()
  290. * @param string $query
  291. * @param array $options
  292. * @return PDOStatement
  293. */
  294. public function prepare($query, $options = array())
  295. {
  296. $this->connect();
  297. if (($stmt = $this->pdo->prepare((string) $query, $options)) === false) {
  298. $this->_errorInfo = $this->pdo->errorInfo();
  299. return false;
  300. }
  301. return $stmt;
  302. }
  303. /**
  304. * Starts a transaction
  305. */
  306. public function beginTransaction()
  307. {
  308. $this->connect();
  309. $this->pdo->beginTransaction();
  310. $this->_inTransaction = true;
  311. }
  312. /**
  313. * Checks if a transaction is being used
  314. *
  315. * @return bool
  316. */
  317. public function isInTransaction()
  318. {
  319. return $this->_inTransaction;
  320. }
  321. /**
  322. * Commits a transaction
  323. */
  324. public function commit()
  325. {
  326. $this->connect();
  327. $this->pdo->commit();
  328. $this->_inTransaction = false;
  329. }
  330. /**
  331. * Rollbacks a transaction
  332. */
  333. public function rollback()
  334. {
  335. $this->connect();
  336. $this->pdo->rollBack();
  337. $this->_inTransaction = false;
  338. }
  339. /**
  340. * Finds the first row matching the arguments
  341. *
  342. * @see Atomik_Db_Instance::findAll()
  343. * @param string $table
  344. * @param array $where
  345. * @param string $orderBy
  346. * @param string $offset
  347. * @param string|array $fields
  348. * @return mixed
  349. */
  350. public function find($table, $where = null, $orderBy = null, $offset = 0, $fields = null)
  351. {
  352. $limit = array($offset, 1);
  353. if (($result = self::findAll($table, $where, $orderBy, $limit, $fields)) === false) {
  354. return false;
  355. }
  356. $row = $result->fetch();
  357. $result->closeCursor();
  358. return $row;
  359. }
  360. /**
  361. * Finds all rows matching the arguments
  362. *
  363. * @see Atomik_Db_Query
  364. * @param string|array $table
  365. * @param array $where
  366. * @param string $orderBy
  367. * @param string $limit
  368. * @param string|array $fields
  369. * @return Atomik_Db_Query_Result
  370. */
  371. public function findAll($table, $where = null, $orderBy = null, $limit = null, $fields = null)
  372. {
  373. $query = $this->_buildQuery($table, $where, $orderBy, $limit, $fields);
  374. return $query->execute();
  375. }
  376. /**
  377. * Returns the value of the specified column of the first row to be found
  378. *
  379. * @see Atomik_Db_Instance::find()
  380. * @param string $table
  381. * @param string $column
  382. * @param array $where
  383. * @param string $orderBy
  384. * @param string $offset
  385. * @return mixed
  386. */
  387. public function findValue($table, $column, $where = null, $orderBy = null, $offset = 0)
  388. {
  389. if (($row = self::find($table, $where, $orderBy, $offset, array($column))) === false) {
  390. return false;
  391. }
  392. if (!array_key_exists($column, $row)) {
  393. return false;
  394. }
  395. return $row[$column];
  396. }
  397. /**
  398. * Perform a SELECT COUNT(*) query
  399. *
  400. * @see Atomik_Db_Instance::buildWhere()
  401. * @param string|Atomik_Db_Query $table
  402. * @param array $where
  403. * @return int
  404. */
  405. public function count($table, $where = null)
  406. {
  407. if (!($table instanceof Atomik_Db_Query)) {
  408. $query = $this->_buildQuery($table, $where, null, null, 'COUNT(*)');
  409. } else {
  410. $query = clone $table;
  411. $query->count();
  412. }
  413. if (($result = $query->execute()) === false) {
  414. return false;
  415. }
  416. $count = $result->fetchColumn();
  417. $result->closeCursor();
  418. return (int) $count;
  419. }
  420. /**
  421. * Checks if some rows exist with the specified $where
  422. * Kinf of an alias of {@see Atomik_Db_Instance::count()}
  423. *
  424. * @param string|array $table
  425. * @param array $where
  426. * @return bool
  427. */
  428. public function has($table, $where)
  429. {
  430. return $this->count($table, $where) > 0;
  431. }
  432. /**
  433. * Inserts a row inside the database.
  434. * $data must be an array where keys are column name
  435. * and their associated value the value to insert in the
  436. * database
  437. *
  438. * @param string $table
  439. * @param array $data
  440. * @return int Last inserted id
  441. */
  442. public function insert($table, $data)
  443. {
  444. $sql = sprintf('INSERT INTO %s (%s) VALUES (%s)',
  445. $table,
  446. implode(', ', array_keys($data)),
  447. implode(', ', array_fill(0, count($data), '?'))
  448. );
  449. $params = array_values($data);
  450. $stmt = $this->prepare($sql);
  451. if (!$stmt->execute($params)) {
  452. $this->_errorInfo = $stmt->errorInfo();
  453. return false;
  454. }
  455. return $this->pdo->lastInsertId();
  456. }
  457. /**
  458. * Updates a row
  459. *
  460. * @see Atomik_Db_Instance::buildWhere()
  461. * @param string $table
  462. * @param array $data
  463. * @param array $where
  464. * @return bool
  465. */
  466. public function update($table, $data, $where)
  467. {
  468. $sql = sprintf('UPDATE %s SET %s = ? WHERE %s = ?',
  469. $table,
  470. implode(' = ?, ', array_keys($data)),
  471. implode(' = ? AND ', array_keys($where))
  472. );
  473. $params = array_merge(array_values($data), array_values($where));
  474. $stmt = $this->prepare($sql);
  475. if (!$stmt->execute($params)) {
  476. $this->_errorInfo = $stmt->errorInfo();
  477. return false;
  478. }
  479. return true;
  480. }
  481. /**
  482. * Deletes rows
  483. *
  484. * @see Atomik_Db_Instance::buildWhere()
  485. * @param array|string $table
  486. * @param array $where
  487. * @return bool
  488. */
  489. public function delete($table, $where)
  490. {
  491. $sql = sprintf('DELETE FROM %s WHERE ', $table);
  492. $params = array();
  493. if (is_array($where)) {
  494. $sql .= sprintf('%s = ?', implode(' = ? AND ', array_keys($where)));
  495. $params = array_values($where);
  496. } else {
  497. $sql .= $where;
  498. }
  499. $stmt = $this->prepare($sql);
  500. if (!$stmt->execute($params)) {
  501. $this->_errorInfo = $stmt->errorInfo();
  502. return false;
  503. }
  504. return true;
  505. }
  506. /**
  507. * Inserts or updates values depending if they're already in the database.
  508. *
  509. * Uses {@see Atomik_Db_Instance::has()} to check if data is already inserted.
  510. * If $where is null, $data will be used as the where clause. $where can also
  511. * be a string representing a key of the data array
  512. *
  513. * @param string $table
  514. * @param array $data
  515. * @param array|string $where
  516. * @return int|bool Last insert id if it's an insert, true for success on update, false otherwise
  517. */
  518. public function set($table, $data, $where = null)
  519. {
  520. if ($where === null) {
  521. $where = $data;
  522. } else if (is_string($where) && array_key_exists($where, $data)) {
  523. $where = array($where => $data[$where]);
  524. } else if (is_array($where)) {
  525. $tmpWhere = $where;
  526. $where = array();
  527. foreach ($tmpWhere as $key => $value) {
  528. if (is_int($key)) {
  529. if (array_key_exists($value, $data)) {
  530. $where[$value] = $data[$value];
  531. }
  532. } else {
  533. $where[$key] = $value;
  534. }
  535. }
  536. } else {
  537. return false;
  538. }
  539. if ($this->has($table, $where)) {
  540. return $this->update($table, $data, $where);
  541. }
  542. return $this->insert($table, $data);
  543. }
  544. /**
  545. * Builds a Atomik_Db_Query object
  546. *
  547. * @see Atomik_Db_Query
  548. * @param string|array $table
  549. * @param array $where
  550. * @param string $orderBy
  551. * @param string $limit
  552. * @param string|array $fields
  553. * @return Atomik_Db_Query
  554. */
  555. protected function _buildQuery($table, $where = null, $orderBy = null, $limit = null, $fields = null)
  556. {
  557. $query = $this->q()->select($fields)->from($table);
  558. if ($where !== null) {
  559. $query->where($where);
  560. }
  561. if ($orderBy !== null) {
  562. $query->orderBy($orderBy);
  563. }
  564. if ($limit !== null) {
  565. $query->limit($limit);
  566. }
  567. return $query;
  568. }
  569. }