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

/src/db/epDb.php

https://bitbucket.org/davidmpaz/ezpdo
PHP | 481 lines | 171 code | 62 blank | 248 comment | 40 complexity | 6153be62351f3c90223f947820cabc50 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0
  1. <?php
  2. /**
  3. * $Id: epDb.php 1044 2007-03-08 02:25:07Z nauhygon $
  4. *
  5. * Copyright(c) 2005 by Oak Nauhygon. All rights reserved.
  6. *
  7. * @author Oak Nauhygon <ezpdo4php@gmail.com>
  8. * @version $Revision: 1044 $ $Date: 2007-03-07 21:25:07 -0500 (Wed, 07 Mar 2007) $
  9. * @package ezpdo
  10. * @subpackage ezpdo.db
  11. */
  12. /**
  13. * need epBase
  14. */
  15. include_once(EP_SRC_BASE.'/epBase.php');
  16. /**
  17. * Exception class for {@link epDb}
  18. *
  19. * @author Oak Nauhygon <ezpdo4php@gmail.com>
  20. * @version $Revision: 1044 $ $Date: 2007-03-07 21:25:07 -0500 (Wed, 07 Mar 2007) $
  21. * @package ezpdo
  22. * @subpackage ezpdo.db
  23. */
  24. class epExceptionDb extends epException {
  25. }
  26. /**
  27. * Class for a database connection
  28. *
  29. * This abstract class provides a unified layer for using different
  30. * database abstract libraries. Subclasses should implement abstract
  31. * method defined here.
  32. *
  33. * Note that this layer does not concern any object persistence
  34. * issues, all of which are taken care of by {@link epDbObject} (a
  35. * wrapper around this class). Also note that we DO NOT deal with
  36. * portability issues in this class. Portabilty issues are dealt
  37. * with in {@link epDbPort}.
  38. *
  39. * This class provides a generic interface only for establishing/
  40. * tearing down connection, executing standard SQL queries, tracking
  41. * insert ids, quoting values/identifiers, etc.
  42. *
  43. * For now, we support both ADODB ({@link epDbAdodb}) and PEAR::DB
  44. * ({@link epDbPeardb}). Experimentally, we now also support PDO
  45. * ({@link http://www.php.net/manual/en/ref.pdo.php}) either through
  46. * the PDO driver in ADODB (see {@link epDbAdodbPdo}) or directly
  47. * (see {@link epDbPdo}).
  48. *
  49. * @author Oak Nauhygon <ezpdo4php@gmail.com>
  50. * @version $Revision: 1044 $ $Date: 2007-03-07 21:25:07 -0500 (Wed, 07 Mar 2007) $
  51. * @package ezpdo
  52. * @subpackage ezpdo.db
  53. */
  54. abstract class epDb {
  55. /**#@+
  56. * Types of databases
  57. */
  58. const EP_DBT_ACCESS = 'Access';
  59. const EP_DBT_DB2 = 'DB2';
  60. const EP_DBT_FIREBIRD = 'Firebird';
  61. const EP_DBT_IBASE = 'Ibase';
  62. const EP_DBT_INFORMIX = 'Informix';
  63. const EP_DBT_MSSQL = 'Mssql';
  64. const EP_DBT_MYSQL = 'Mysql';
  65. const EP_DBT_OCI8 = 'Oci8';
  66. const EP_DBT_POSTGRES = 'Postgres';
  67. const EP_DBT_SAPDB = 'Sapdb';
  68. const EP_DBT_SQLITE = 'Sqlite';
  69. const EP_DBT_SYBASE = 'Sybase';
  70. /**#@-*/
  71. /**
  72. * Name quotes for different db types
  73. * @var array
  74. */
  75. static protected $nameQuotes = array(
  76. epDb::EP_DBT_MYSQL => '`',
  77. epDb::EP_DBT_POSTGRES => '"',
  78. epDb::EP_DBT_SQLITE => '"',
  79. );
  80. /**
  81. * The PEAR-style DSN for the database
  82. * @var string
  83. * @see http://pear.php.net/manual/en/package.database.db.intro-dsn.php
  84. */
  85. protected $dsn = false;
  86. /**
  87. * The db connection that the underlying db lib supports
  88. * @var mixed
  89. */
  90. public $db = false;
  91. /**
  92. * Flag that db is currently in transaction
  93. * @var bool
  94. */
  95. public $in_transaction = false;
  96. /**
  97. * Whether to debug db or not
  98. * If true, queries will be collected
  99. * @var boolean
  100. */
  101. protected $log_queries = false;
  102. /**
  103. * Whether to debug db or not
  104. * @var boolean
  105. */
  106. protected $queries = array();
  107. /**
  108. * The db type (EP_DBT_XXX consts below)
  109. * @var string
  110. */
  111. public $dbtype = false;
  112. /**
  113. * The name quote for the current db type
  114. */
  115. protected $nameQuote = false;
  116. /**
  117. * Array of tables exist
  118. */
  119. protected $tables_exist = array();
  120. /**
  121. * Constructor
  122. * @param string $dsn the foreign class name
  123. * @param boolean $log_queries whether to log queries (for debugging purpose)
  124. * @see epOverload::__construct()
  125. */
  126. public function __construct($dsn, $log_queries = false) {
  127. // check dsn
  128. if (empty($dsn)) {
  129. throw new epExceptionDb('DSN empty');
  130. }
  131. $this->dsn = $dsn;
  132. $this->log_queries = $log_queries;
  133. }
  134. /**
  135. * Returns the database type for this connection
  136. * @return string
  137. * @throws epExceptionDb
  138. */
  139. public function dbType() {
  140. if (!$this->dbtype) {
  141. // use epDbDsn to parse PEAR DSN
  142. include_once(EP_SRC_DB . '/epDbDsn.php');
  143. if (!($d = new epDbDsn($this->dsn))) {
  144. throw new epExceptionDb('Error in parsing DSN');
  145. }
  146. // set db type
  147. $this->_setDbType($d['phptype'] . ':' . $d['dbsyntax']);
  148. }
  149. return $this->dbtype;
  150. }
  151. /**
  152. * Set the database type from driver/phptype string
  153. * @param string $t (the driver/phptype string)
  154. * @return bool
  155. */
  156. protected function _setDbType($t) {
  157. if (stristr($t, 'access')) {
  158. $this->dbtype = self::EP_DBT_ACCESS;
  159. } else if (stristr($t, 'db2')) {
  160. $this->dbtype = self::EP_DBT_DB2;
  161. } else if (stristr($t, 'firebird')) {
  162. $this->dbtype = self::EP_DBT_FIREBIRD;
  163. } else if (stristr($t, 'ibase')) {
  164. $this->dbtype = self::EP_DBT_IBASE;
  165. } else if (stristr($t, 'informix') || stristr($t, 'ifx')) {
  166. $this->dbtype = self::EP_DBT_INFORMIX;
  167. } else if (stristr($t, 'mssql')) {
  168. $this->dbtype = self::EP_DBT_MSSQL;
  169. } else if (stristr($t, 'mysql')) {
  170. $this->dbtype = self::EP_DBT_MYSQL;
  171. } else if (stristr($t, 'oci8')) {
  172. $this->dbtype = self::EP_DBT_OCI8;
  173. } else if (stristr($t, 'pgsql')) {
  174. $this->dbtype = self::EP_DBT_POSTGRES;
  175. } else if (stristr($t, 'sqlite')) {
  176. $this->dbtype = self::EP_DBT_SQLITE;
  177. } else if (stristr($t, 'sybase')) {
  178. $this->dbtype = self::EP_DBT_SYBASE;
  179. } else {
  180. return false;
  181. }
  182. return true;
  183. }
  184. /**
  185. * Establishes a DB connection
  186. * @access public
  187. * @return bool
  188. */
  189. abstract public function open();
  190. /**
  191. * Closes the DB connection
  192. * Subclass must override this method
  193. * @return void
  194. */
  195. abstract public function close();
  196. /**
  197. * Check if a table exists
  198. * @param string $table
  199. * @return bool
  200. */
  201. abstract public function tableExists($table);
  202. /**
  203. * Clears table from table_exists cache
  204. * @param string $table
  205. * @return void
  206. */
  207. public function clearTableExists($table) {
  208. unset($this->tables_exist[$table]);
  209. }
  210. /**
  211. * Set whether to log queries
  212. * @param boolean $log_queries
  213. * @return boolean
  214. */
  215. public function logQueries($log_queries = true) {
  216. return $this->log_queries = $log_queries;
  217. }
  218. /**
  219. * Returns queries logged. If reset flag is set
  220. * (default), empty logged queries.
  221. * @param boolean $reset
  222. * @return array
  223. */
  224. public function getQueries($reset = true) {
  225. $queries = $this->queries;
  226. if ($reset) {
  227. $this->queries = array();
  228. }
  229. return $queries;
  230. }
  231. /**
  232. * Returns whether db is in transaction
  233. * @return bool
  234. */
  235. public function inTransaction() {
  236. return $this->in_transaction;
  237. }
  238. /**
  239. * Turns off autocommit mode. While autocommit mode is turned off,
  240. * changes made to the database are not committed until you end
  241. * the transaction by calling either {@link commit()}.
  242. * @return bool
  243. */
  244. public function beginTransaction() {
  245. if ($this->in_transaction) {
  246. return false;
  247. }
  248. $status = $this->_beginTransaction();
  249. $this->in_transaction = true;
  250. return $status;
  251. }
  252. /**
  253. * Acutally start a transaction by calling the underlying
  254. * database connection
  255. * @return bool
  256. */
  257. abstract protected function _beginTransaction();
  258. /**
  259. * Commits a transaction, returning the database connection to
  260. * autocommit mode until the next call to {@link beginTransaction()}
  261. * starts a new transaction.
  262. * @return bool
  263. */
  264. public function commit() {
  265. if (!$this->in_transaction) {
  266. return false;
  267. }
  268. $status = $this->_commit();
  269. $this->in_transaction = false;
  270. return (boolean)$status;
  271. }
  272. /**
  273. * Actually commit the current transaction by calling the underlying
  274. * database connection
  275. * @return bool
  276. */
  277. abstract protected function _commit();
  278. /**
  279. * Rolls back the current transaction, as initiated by
  280. * {@link beginTransaction()}. It is an error to call this method if no
  281. * transaction is active. If the database was set to autocommit mode,
  282. * this function will restore autocommit mode after it has rolled
  283. * back the transaction.
  284. * @return bool
  285. */
  286. public function rollback() {
  287. if (!$this->in_transaction) {
  288. return false;
  289. }
  290. $status = $this->_rollback();
  291. $this->in_transaction = false;
  292. return $status;
  293. }
  294. /**
  295. * Acutally rollback the current transaction by calling the underlying
  296. * database connection
  297. * @return bol
  298. */
  299. abstract protected function _rollback();
  300. /**
  301. * Executes SQL command
  302. * Subclass must override this method
  303. * @param string query string
  304. * @return mixed
  305. * @access public
  306. */
  307. public function execute($query) {
  308. if ($this->log_queries) {
  309. $t = microtime(true);
  310. }
  311. $r = $this->_execute($query);
  312. if ($this->log_queries) {
  313. $t = microtime(true) - $t;
  314. $this->queries[] = '[' . $t . '] ' . $query;
  315. }
  316. return $r;
  317. }
  318. /**
  319. * Executes SQL command (only called by execute())
  320. * Subclass must override this method
  321. * @param string query string
  322. * @return mixed
  323. * @access public
  324. */
  325. abstract protected function _execute($query);
  326. /**
  327. * Returns the last insert id
  328. * @param string $oid the oid column
  329. * @return integer
  330. * @access public
  331. */
  332. abstract public function lastInsertId($table, $oid = 'oid');
  333. /**
  334. * Returns the number of records in last result set
  335. * @param mixed $rs the result set
  336. * @return integer
  337. */
  338. abstract public function rsRows();
  339. /**
  340. * Rewinds to the first row in the last result
  341. * @return bool
  342. */
  343. abstract public function rsRestart();
  344. /**
  345. * Moves to the next row in the last result set
  346. * @return bool
  347. */
  348. abstract public function rsNext();
  349. /**
  350. * Get the value for a column in the current row in the last result set
  351. * @param string $col the name of the column
  352. * @param string $col_alt the alternative name of the column (used only when $col fails)
  353. * @return false|mixed
  354. */
  355. abstract public function rsGetCol($col, $col_alt = false);
  356. /**
  357. * Formats input so it can be safely used as a literal
  358. * @param mixed $input
  359. * @return mixed
  360. */
  361. abstract public function quote($input);
  362. /**
  363. * Formats a string so it can be safely used as an identifier (e.g. table, column names)
  364. * @param mixed $input
  365. * @return mixed
  366. */
  367. public function quoteId($input) {
  368. // [kludge] sqlite bug - doesn't like "xxx"."*"
  369. if (strpos($input, '.*') && $this->dbType() == self::EP_DBT_SQLITE) {
  370. return $input;
  371. }
  372. // the quoting symbol
  373. $q = $this->_getNameQuote();
  374. // split input into items (xx.yy.zz)
  375. $quoted = array();
  376. foreach(explode('.', $input) as $item) {
  377. if ($item != '*') {
  378. $quoted[] = $q . $item . $q;
  379. } else {
  380. $quoted[] = $item;
  381. }
  382. }
  383. // quote them into ('xx'.'yy'.'zz')
  384. return implode('.', $quoted);
  385. }
  386. /**
  387. * Gets the data source name (DSN) of the database connection
  388. * @return string
  389. * @access public
  390. */
  391. public function dsn() {
  392. return $this->dsn;
  393. }
  394. /**
  395. * Returns the db connection - an instance of db connection
  396. * that the underlying db library supports. Not supposed to
  397. * be called directly. Provided for testing only.
  398. * @return mixed
  399. */
  400. public function &connection() {
  401. return $this->db;
  402. }
  403. /**
  404. * Returns the name quote char for the curent db type
  405. * @return string
  406. */
  407. protected function _getNameQuote() {
  408. // have we gotten the name quote yet?
  409. if ($this->nameQuote === false) {
  410. $this->nameQuote = ''; // default to empty string
  411. if (isset(self::$nameQuotes[$dbtype = $this->dbType()])) {
  412. $this->nameQuote = self::$nameQuotes[$dbtype];
  413. }
  414. }
  415. // return name quote
  416. return $this->nameQuote;
  417. }
  418. }
  419. ?>