PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/pear/PDB/tags/0.0.4/PDB/Common.php

http://digg.googlecode.com/
PHP | 533 lines | 149 code | 36 blank | 348 comment | 11 complexity | 2f251241f3abb19101988739f1ef73e1 MD5 | raw file
  1. <?php
  2. /**
  3. * Base PDB class
  4. *
  5. * PHP version 5.2+
  6. *
  7. * Copyright (c) 2007, 2008, Digg, Inc.
  8. *
  9. * All rights reserved.
  10. *
  11. * Redistribution and use in source and binary forms, with or without
  12. * modification, are permitted provided that the following conditions are met:
  13. *
  14. * - Redistributions of source code must retain the above copyright notice,
  15. * this list of conditions and the following disclaimer.
  16. * - Redistributions in binary form must reproduce the above copyright notice,
  17. * this list of conditions and the following disclaimer in the documentation
  18. * and/or other materials provided with the distribution.
  19. * - Neither the name of the Digg, INc. nor the names of its contributors
  20. * may be used to endorse or promote products derived from this software
  21. * without specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. *
  35. * @category DB
  36. * @package PDB
  37. * @author Joe Stump <joe@joestump.net>
  38. * @copyright 2007-2008 (c) Digg.com
  39. * @license http://tinyurl.com/42zef New BSD License
  40. * @version CVS: $Id:$
  41. * @link http://www.php.net/pdo
  42. * @link http://pear.php.net/package/PDB
  43. * @filesource
  44. */
  45. require_once 'PDB/Exception.php';
  46. require_once 'PDB/Result.php';
  47. /**
  48. * Base PDB class
  49. *
  50. * @category DB
  51. * @package PDB
  52. * @author Joe Stump <joe@joestump.net>
  53. * @copyright 2007-2008 (c) Digg.com
  54. * @license http://tinyurl.com/42zef New BSD License
  55. * @version Release: @package_version@
  56. * @link http://pear.php.net/package/PDB
  57. */
  58. abstract class PDB_Common
  59. {
  60. /**
  61. * The PDO connection
  62. *
  63. * Due to various issues with PDO (e.g. the inability to disconnect)
  64. * we use the decorator pattern to envelope PDO with extra
  65. * functionality.
  66. *
  67. * @var object $pdo Instance of PDO
  68. * @link http://us.php.net/pdo
  69. * @see PDB_Common::__call()
  70. */
  71. protected $pdo = null;
  72. /**
  73. * PDO DSN
  74. *
  75. * @access protected
  76. * @var string $dsn PDO DSN (e.g. mysql:host=127.0.0.1;dbname=foo)
  77. */
  78. protected $dsn = '';
  79. /**
  80. * DNS info as an stdClass
  81. *
  82. * @var stdClass
  83. */
  84. protected $dsnObject = null;
  85. /**
  86. * Username for DB connection
  87. *
  88. * @access protected
  89. * @var string $username DB username
  90. */
  91. protected $user = '';
  92. /**
  93. * Password for DB connection
  94. *
  95. * @access protected
  96. * @var string $password DB password
  97. */
  98. protected $pass = '';
  99. /**
  100. * PDO/Driver options
  101. *
  102. * @access protected
  103. * @var array $options PDO/Driver options
  104. * @link http://us.php.net/manual/en/pdo.constants.php
  105. * @link http://us.php.net/manual/en/pdo.drivers.php
  106. */
  107. protected $options = array();
  108. /**
  109. * Default fetch mode
  110. *
  111. * @access private
  112. * @var int $fetchMode
  113. */
  114. public $fetchMode = PDO::FETCH_NUM;
  115. /**
  116. * Constructor
  117. *
  118. * @param string $dsn The PDO DSN
  119. * @param string $username The DB's username
  120. * @param string $password The DB's password
  121. * @param array $options PDO/driver options array
  122. *
  123. * @return void
  124. * @see PDB_Common::connect(), PDB_Common::$dsn, PDB_Common::$username
  125. * @see PDB_Common::$password, PDB_Common::$options
  126. */
  127. public function __construct($dsn,
  128. $username = '',
  129. $password = '',
  130. $options = array())
  131. {
  132. $this->dsn = $dsn;
  133. $this->user = $username;
  134. $this->pass = $password;
  135. $this->options = $options;
  136. $this->connect();
  137. }
  138. /**
  139. * Connect to the database
  140. *
  141. * @return void
  142. * @see PDB_Common::$dsn, PDB_Common::$username
  143. * @see PDB_Common::$password, PDB_Common::$options
  144. * @see PDB_Common::setAttribute
  145. */
  146. public function connect()
  147. {
  148. $this->pdo = new PDO($this->dsn,
  149. $this->user,
  150. $this->pass,
  151. $this->options);
  152. $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  153. }
  154. /**
  155. * Reconnect to the database
  156. *
  157. * This reconnects to the database with the given parameters from
  158. * before we either disconnected or lost the connection. This is useful
  159. * for when MySQL (and others probably) servers "go away".
  160. *
  161. * @see PDB_Common::disconnect(), PDB_Common::connect()
  162. * @return void
  163. */
  164. public function reconnect()
  165. {
  166. $this->disconnect();
  167. $this->connect();
  168. }
  169. /**
  170. * Disconnect from the DB
  171. *
  172. * @return void
  173. */
  174. public function disconnect()
  175. {
  176. $this->pdo = null;
  177. }
  178. /**
  179. * Get DSN as an stdClass
  180. *
  181. * @return stdClass The DNS info in an stdClass
  182. */
  183. public function getDSN() {
  184. if ($this->dsnObject == null) {
  185. list($type, $parseMe) = explode(':', $this->dsn);
  186. $parseMe = str_replace(';', '&', $parseMe);
  187. $dsnParts = array();
  188. parse_str($parseMe, $dsnParts);
  189. $dsnParts['name'] = $dsnParts['dbname'];
  190. $dsnParts['user'] = $this->user;
  191. $dsnParts['pass'] = $this->pass;
  192. $dsnParts['type'] = $type;
  193. unset($dsnParts['dbname']);
  194. $this->dsnObject = (object) $dsnParts;
  195. }
  196. return $this->dsnObject;
  197. }
  198. /**
  199. * Implement decorator pattern
  200. *
  201. * Originally {@link PDB} was extended from PDO, but this kept us from
  202. * implementing valid {@link PDB_Common::disconnect()} and
  203. * {@link PDB_Common::reconnect()} methods, which were needed for other
  204. * nice functionality.
  205. *
  206. * As a result we use {@link PDB_Common::__call()} to implement the basic
  207. * decorator pattern. Everything listed below should work without issues.
  208. *
  209. * @param string $function Name of function to run
  210. * @param array $args Function's arguments
  211. *
  212. * @method bool beginTransaction()
  213. * @method bool commit()
  214. * @method string errorCode()
  215. * @method array errorInfo()
  216. * @method int exec(string $statement)
  217. * @method mixed getAttribute(int $attribute)
  218. * @method string lastInsertId([string $name])
  219. * @method PDOStatement prepare(string $statement [, array $driver_options])
  220. * @method string quote(string $string [, int $parameter_type])
  221. * @method bool rollBack()
  222. * @return mixed
  223. */
  224. public function __call($function, array $args = array())
  225. {
  226. if (is_null($this->pdo)) {
  227. throw new PDB_Exception('Not connected to DB');
  228. }
  229. return call_user_func_array(array($this->pdo, $function), $args);
  230. }
  231. /**
  232. * Query the database
  233. *
  234. * <code>
  235. * <?php
  236. *
  237. * require_once 'PDB.php';
  238. *
  239. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  240. * $db->setFetchMode(PDO::FETCH_OBJECT);
  241. *
  242. * $sql = 'SELECT *
  243. * FROM items
  244. * WHERE promoted = ? AND
  245. * userid = ?';
  246. *
  247. * $result = $db->query($sql, array(1, (int)$_GET['userid']));
  248. *
  249. * // Notice that {@link PDB_Result} supports object iteration just like
  250. * // PDOStatement does since it extends from it.
  251. * foreach ($result as $row) {
  252. * echo '<a href="' . $row->url . '">' . $row->title . '</a>' . "\n";
  253. * }
  254. *
  255. * ?>
  256. * </code>
  257. *
  258. * @param string $sql The query
  259. * @param array $args The query arguments
  260. *
  261. * @return object Instance of {@link PDB_Result}
  262. * @throws {@link PDB_Exception} on failure
  263. * @link http://us3.php.net/manual/en/class.pdostatement.php
  264. * @link http://us3.php.net/manual/en/pdostatement.bindparam.php
  265. */
  266. public function query($sql, array $args = array())
  267. {
  268. try {
  269. $stmt = $this->prepare($sql, array(
  270. PDO::ATTR_STATEMENT_CLASS => array(
  271. 'PDB_Result', array($this->pdo, $this->fetchMode)
  272. )
  273. ));
  274. if (is_array($args)) {
  275. $cnt = count($args);
  276. if ($cnt > 0) {
  277. foreach ($args as $key => $value) {
  278. $param = (is_int($key) ? ($key + 1) : $key);
  279. $result = $stmt->bindParam($param, $args[$key]);
  280. }
  281. }
  282. }
  283. $stmt->execute();
  284. return $stmt;
  285. } catch (PDOException $error) {
  286. throw new PDB_Exception($error->getMessage(), (int) $error->getCode());
  287. }
  288. }
  289. /**
  290. * Fetch a single row
  291. *
  292. * <code>
  293. * <?php
  294. *
  295. * require_once 'PDB.php';
  296. *
  297. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  298. * $db->setFetchMode(PDO::FETCH_OBJECT);
  299. *
  300. * $sql = 'SELECT *
  301. * FROM users
  302. * WHERE userid = ?';
  303. *
  304. * $user = $db->getRow($sql, array((int)$_GET['userid']));
  305. * echo 'Welcome back, ' . $user->username . '!';
  306. *
  307. * ?>
  308. * </code>
  309. *
  310. * @param string $sql The query to run
  311. * @param array $params The query parameter values
  312. * @param integer $fetchMode The fetch mode for query
  313. *
  314. * @see PDB_Common::query(), PDB_Result
  315. * @return array
  316. */
  317. public function getRow($sql,
  318. array $params = array(),
  319. $fetchMode = null)
  320. {
  321. if (is_null($fetchMode)) {
  322. $fetchMode = $this->fetchMode;
  323. }
  324. $result = $this->query($sql, $params);
  325. return $result->fetchRow($fetchMode);
  326. }
  327. /**
  328. * Fetch a single column
  329. *
  330. * <code>
  331. * <?php
  332. *
  333. * require_once 'PDB.php';
  334. *
  335. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  336. * $sql = 'SELECT friendid
  337. * FROM friends
  338. * WHERE userid = ?';
  339. *
  340. * $friends = $db->getCol($sql, 0, array((int)$_GET['userid']));
  341. * if (in_array($_SESSION['userid'], $friends)) {
  342. * echo 'You are friends with this user!';
  343. * }
  344. *
  345. * ?>
  346. * </code>
  347. *
  348. * @param string $sql The query to run
  349. * @param integer $col The column number to fetch (zero-based)
  350. * @param array $params The query parameter values
  351. *
  352. * @see PDB_Common::query(), PDB_Result
  353. * @return array
  354. */
  355. public function getCol($sql, $col = 0, array $params = array())
  356. {
  357. $result = $this->query($sql, $params);
  358. $ret = array();
  359. while ($row = $result->fetchRow(PDO::FETCH_NUM)) {
  360. $ret[] = $row[$col];
  361. }
  362. return $ret;
  363. }
  364. /**
  365. * Fetch all records in query as array
  366. *
  367. * This method will fetch all records from a given query into a
  368. * numerically indexed array (e.g. $result[0] is the first record).
  369. *
  370. * <code>
  371. * <?php
  372. *
  373. * require_once 'PDB.php';
  374. *
  375. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  376. * $db->setFetchMode(PDO::FETCH_OBJECT);
  377. *
  378. * $sql = 'SELECT *
  379. * FROM users
  380. * WHERE type = ?';
  381. *
  382. * $students = $db->getAll($sql, array('student'));
  383. * foreach ($students as $student) {
  384. * echo $student->firstname . "\n";
  385. * }
  386. *
  387. * ?>
  388. * </code>
  389. *
  390. * @param string $sql The query to run
  391. * @param array $params The query parameter values
  392. * @param integer $fetchMode The fetch mode for query
  393. *
  394. * @return array
  395. * @see PDB_Result, PDB_Common::query()
  396. */
  397. public function getAll($sql,
  398. array $params = array(),
  399. $fetchMode = null)
  400. {
  401. if (is_null($fetchMode)) {
  402. $fetchMode = $this->fetchMode;
  403. }
  404. $result = $this->query($sql, $params);
  405. $ret = array();
  406. while ($row = $result->fetchRow($fetchMode)) {
  407. $ret[] = $row;
  408. }
  409. return $ret;
  410. }
  411. /**
  412. * Get a single field
  413. *
  414. * This will fetch a single value from the first row's first
  415. * column.
  416. *
  417. * <code>
  418. * <?php
  419. *
  420. * require_once 'PDB.php';
  421. *
  422. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  423. * $sql = 'SELECT COUNT(*) AS total
  424. * FROM users
  425. * WHERE type = ?';
  426. *
  427. * $total = $db->getOne($sql, array('student'));
  428. * if (!$total) {
  429. * echo 'No students!';
  430. * }
  431. *
  432. * ?>
  433. * </code>
  434. *
  435. * @param string $sql The query to run
  436. * @param array $params The query parameter values
  437. *
  438. * @see PDB_Common::query(), PDB_Result::fetchRow()
  439. * @return mixed The value of the first row/column
  440. */
  441. public function getOne($sql, array $params = array())
  442. {
  443. $result = $this->query($sql, $params);
  444. $row = $result->fetchRow(PDO::FETCH_NUM);
  445. return $row[0];
  446. }
  447. /**
  448. * Set the fetch mode for all queries
  449. *
  450. * This should be set to one of PDO's fetch modes. Valid values include:
  451. * - PDO::FETCH_LAZY
  452. * - PDO::FETCH_ASSOC
  453. * - PDO::FETCH_NAMED
  454. * - PDO::FETCH_NUM
  455. * - PDO::FETCH_BOTH
  456. * - PDO::FETCH_OBJ
  457. *
  458. * @param integer $mode The DB fetch mode
  459. *
  460. * @throws UnexpectedArgumentException on invalid modes
  461. * @access public
  462. * @return void
  463. */
  464. public function setFetchMode($mode)
  465. {
  466. switch ($mode) {
  467. case PDO::FETCH_LAZY:
  468. case PDO::FETCH_ASSOC:
  469. case PDO::FETCH_NAMED:
  470. case PDO::FETCH_NUM:
  471. case PDO::FETCH_BOTH:
  472. case PDO::FETCH_OBJ:
  473. $this->fetchMode = $mode;
  474. break;
  475. default:
  476. throw UnexpectedArgumentException('Invalid mode');
  477. }
  478. }
  479. /**
  480. * Set an attribute
  481. *
  482. * @param integer $attribute The attribute to set
  483. * @param mixed $value The attribute's value
  484. *
  485. * @link http://us.php.net/manual/en/pdo.setattribute.php
  486. * @return true False if something failed to set
  487. */
  488. public function setAttribute($attribute, $value)
  489. {
  490. if ($this->pdo->setAttribute($attribute, $value)) {
  491. $this->options[$attribute] = $value;
  492. return true;
  493. }
  494. return false;
  495. }
  496. }
  497. ?>