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

/pear/PDB/tags/0.0.2/PDB/Common.php

http://digg.googlecode.com/
PHP | 501 lines | 133 code | 30 blank | 338 comment | 9 complexity | 2aea8ea0710203fb3593d8d9099e80ee MD5 | raw file
  1. <?php
  2. /**
  3. * Base PDB class
  4. *
  5. * PHP version 5.2+
  6. *
  7. * Copyright (c) 2007, 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. * Username for DB connection
  81. *
  82. * @access protected
  83. * @var string $username DB username
  84. */
  85. protected $username = '';
  86. /**
  87. * Password for DB connection
  88. *
  89. * @access protected
  90. * @var string $password DB password
  91. */
  92. protected $password = '';
  93. /**
  94. * PDO/Driver options
  95. *
  96. * @access protected
  97. * @var array $options PDO/Driver options
  98. * @link http://us.php.net/manual/en/pdo.constants.php
  99. * @link http://us.php.net/manual/en/pdo.drivers.php
  100. */
  101. protected $options = array();
  102. /**
  103. * Default fetch mode
  104. *
  105. * @access private
  106. * @var int $fetchMode
  107. */
  108. public $fetchMode = PDO::FETCH_NUM;
  109. /**
  110. * Constructor
  111. *
  112. * @param string $dsn The PDO DSN
  113. * @param string $username The DB's username
  114. * @param string $password The DB's password
  115. * @param array $options PDO/driver options array
  116. *
  117. * @return void
  118. * @see PDB_Common::connect(), PDB_Common::$dsn, PDB_Common::$username
  119. * @see PDB_Common::$password, PDB_Common::$options
  120. */
  121. public function __construct($dsn,
  122. $username = '',
  123. $password = '',
  124. $options = array())
  125. {
  126. $this->dsn = $dsn;
  127. $this->username = $username;
  128. $this->password = $password;
  129. $this->options = $options;
  130. $this->connect();
  131. }
  132. /**
  133. * Connect to the database
  134. *
  135. * @return void
  136. * @see PDB_Common::$dsn, PDB_Common::$username
  137. * @see PDB_Common::$password, PDB_Common::$options
  138. * @see PDB_Common::setAttribute
  139. */
  140. public function connect()
  141. {
  142. $this->pdo = new PDO($this->dsn,
  143. $this->username,
  144. $this->password,
  145. $this->options);
  146. $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  147. }
  148. /**
  149. * Reconnect to the database
  150. *
  151. * This reconnects to the database with the given parameters from
  152. * before we either disconnected or lost the connection. This is useful
  153. * for when MySQL (and others probably) servers "go away".
  154. *
  155. * @see PDB_Common::disconnect(), PDB_Common::connect()
  156. * @return void
  157. */
  158. public function reconnect()
  159. {
  160. $this->disconnect();
  161. $this->connect();
  162. }
  163. /**
  164. * Disconnect from the DB
  165. *
  166. * @return void
  167. */
  168. public function disconnect()
  169. {
  170. $this->pdo = null;
  171. }
  172. /**
  173. * Implement decorator pattern
  174. *
  175. * Originally {@link PDB} was extended from PDO, but this kept us from
  176. * implementing valid {@link PDB_Common::disconnect()} and
  177. * {@link PDB_Common::reconnect()} methods, which were needed for other
  178. * nice functionality.
  179. *
  180. * As a result we use {@link PDB_Common::__call()} to implement the basic
  181. * decorator pattern. Everything listed below should work without issues.
  182. *
  183. * @param string $function Name of function to run
  184. * @param array $args Function's arguments
  185. *
  186. * @method bool beginTransaction()
  187. * @method bool commit()
  188. * @method string errorCode()
  189. * @method array errorInfo()
  190. * @method int exec(string $statement)
  191. * @method mixed getAttribute(int $attribute)
  192. * @method string lastInsertId([string $name])
  193. * @method PDOStatement prepare(string $statement [, array $driver_options])
  194. * @method string quote(string $string [, int $parameter_type])
  195. * @method bool rollBack()
  196. * @return mixed
  197. */
  198. public function __call($function, array $args = array())
  199. {
  200. if (is_null($this->pdo)) {
  201. throw new PDB_Exception('Not connected to DB');
  202. }
  203. return call_user_func_array(array($this->pdo, $function), $args);
  204. }
  205. /**
  206. * Query the database
  207. *
  208. * <code>
  209. * <?php
  210. *
  211. * require_once 'PDB.php';
  212. *
  213. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  214. * $db->setFetchMode(PDO::FETCH_OBJECT);
  215. *
  216. * $sql = 'SELECT *
  217. * FROM items
  218. * WHERE promoted = ? AND
  219. * userid = ?';
  220. *
  221. * $result = $db->query($sql, array(1, (int)$_GET['userid']));
  222. *
  223. * // Notice that {@link PDB_Result} supports object iteration just like
  224. * // PDOStatement does since it extends from it.
  225. * foreach ($result as $row) {
  226. * echo '<a href="' . $row->url . '">' . $row->title . '</a>' . "\n";
  227. * }
  228. *
  229. * ?>
  230. * </code>
  231. *
  232. * @param string $sql The query
  233. * @param array $args The query arguments
  234. *
  235. * @return object Instance of {@link PDB_Result}
  236. * @throws {@link PDB_Exception} on failure
  237. * @link http://us3.php.net/manual/en/class.pdostatement.php
  238. * @link http://us3.php.net/manual/en/pdostatement.bindparam.php
  239. */
  240. public function query($sql, array $args = array())
  241. {
  242. try {
  243. $stmt = $this->prepare($sql, array(
  244. PDO::ATTR_STATEMENT_CLASS => array(
  245. 'PDB_Result', array($this->pdo, $this->fetchMode)
  246. )
  247. ));
  248. if (is_array($args)) {
  249. $cnt = count($args);
  250. if ($cnt > 0) {
  251. foreach ($args as $key => $value) {
  252. $param = (is_int($key) ? ($key + 1) : $key);
  253. $result = $stmt->bindParam($param, $args[$key]);
  254. }
  255. }
  256. }
  257. $stmt->execute();
  258. return $stmt;
  259. } catch (PDOException $error) {
  260. throw new PDB_Exception($error->getMessage(), $error->getCode());
  261. }
  262. }
  263. /**
  264. * Fetch a single row
  265. *
  266. * <code>
  267. * <?php
  268. *
  269. * require_once 'PDB.php';
  270. *
  271. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  272. * $db->setFetchMode(PDO::FETCH_OBJECT);
  273. *
  274. * $sql = 'SELECT *
  275. * FROM users
  276. * WHERE userid = ?';
  277. *
  278. * $user = $db->getRow($sql, array((int)$_GET['userid']));
  279. * echo 'Welcome back, ' . $user->username . '!';
  280. *
  281. * ?>
  282. * </code>
  283. *
  284. * @param string $sql The query to run
  285. * @param array $params The query parameter values
  286. * @param integer $fetchMode The fetch mode for query
  287. *
  288. * @see PDB_Common::query(), PDB_Result
  289. * @return array
  290. */
  291. public function getRow($sql,
  292. array $params = array(),
  293. $fetchMode = null)
  294. {
  295. if (is_null($fetchMode)) {
  296. $fetchMode = $this->fetchMode;
  297. }
  298. $result = $this->query($sql, $params);
  299. return $result->fetchRow($fetchMode);
  300. }
  301. /**
  302. * Fetch a single column
  303. *
  304. * <code>
  305. * <?php
  306. *
  307. * require_once 'PDB.php';
  308. *
  309. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  310. * $sql = 'SELECT friendid
  311. * FROM friends
  312. * WHERE userid = ?';
  313. *
  314. * $friends = $db->getCol($sql, 0, array((int)$_GET['userid']));
  315. * if (in_array($_SESSION['userid'], $friends)) {
  316. * echo 'You are friends with this user!';
  317. * }
  318. *
  319. * ?>
  320. * </code>
  321. *
  322. * @param string $sql The query to run
  323. * @param integer $col The column number to fetch (zero-based)
  324. * @param array $params The query parameter values
  325. *
  326. * @see PDB_Common::query(), PDB_Result
  327. * @return array
  328. */
  329. public function getCol($sql, $col = 0, array $params = array())
  330. {
  331. $result = $this->query($sql, $params);
  332. $ret = array();
  333. while ($row = $result->fetchRow(PDO::FETCH_NUM)) {
  334. $ret[] = $row[$col];
  335. }
  336. return $ret;
  337. }
  338. /**
  339. * Fetch all records in query as array
  340. *
  341. * This method will fetch all records from a given query into a
  342. * numerically indexed array (e.g. $result[0] is the first record).
  343. *
  344. * <code>
  345. * <?php
  346. *
  347. * require_once 'PDB.php';
  348. *
  349. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  350. * $db->setFetchMode(PDO::FETCH_OBJECT);
  351. *
  352. * $sql = 'SELECT *
  353. * FROM users
  354. * WHERE type = ?';
  355. *
  356. * $students = $db->getAll($sql, array('student'));
  357. * foreach ($students as $student) {
  358. * echo $student->firstname . "\n";
  359. * }
  360. *
  361. * ?>
  362. * </code>
  363. *
  364. * @param string $sql The query to run
  365. * @param array $params The query parameter values
  366. * @param integer $fetchMode The fetch mode for query
  367. *
  368. * @return array
  369. * @see PDB_Result, PDB_Common::query()
  370. */
  371. public function getAll($sql,
  372. array $params = array(),
  373. $fetchMode = null)
  374. {
  375. if (is_null($fetchMode)) {
  376. $fetchMode = $this->fetchMode;
  377. }
  378. $result = $this->query($sql, $params);
  379. $ret = array();
  380. while ($row = $result->fetchRow($fetchMode)) {
  381. $ret[] = $row;
  382. }
  383. return $ret;
  384. }
  385. /**
  386. * Get a single field
  387. *
  388. * This will fetch a single value from the first row's first
  389. * column.
  390. *
  391. * <code>
  392. * <?php
  393. *
  394. * require_once 'PDB.php';
  395. *
  396. * $db = PDB::connect('mysql:host=127.0.0.1;dbname=foo', 'user', 'pass');
  397. * $sql = 'SELECT COUNT(*) AS total
  398. * FROM users
  399. * WHERE type = ?';
  400. *
  401. * $total = $db->getOne($sql, array('student'));
  402. * if (!$total) {
  403. * echo 'No students!';
  404. * }
  405. *
  406. * ?>
  407. * </code>
  408. *
  409. * @param string $sql The query to run
  410. * @param array $params The query parameter values
  411. *
  412. * @see PDB_Common::query(), PDB_Result::fetchRow()
  413. * @return mixed The value of the first row/column
  414. */
  415. public function getOne($sql, array $params = array())
  416. {
  417. $result = $this->query($sql, $params);
  418. $row = $result->fetchRow(PDO::FETCH_NUM);
  419. return $row[0];
  420. }
  421. /**
  422. * Set the fetch mode for all queries
  423. *
  424. * This should be set to one of PDO's fetch modes. Valid values include:
  425. * - PDO::FETCH_LAZY
  426. * - PDO::FETCH_ASSOC
  427. * - PDO::FETCH_NAMED
  428. * - PDO::FETCH_NUM
  429. * - PDO::FETCH_BOTH
  430. * - PDO::FETCH_OBJ
  431. *
  432. * @param integer $mode The DB fetch mode
  433. *
  434. * @throws UnexpectedArgumentException on invalid modes
  435. * @access public
  436. * @return void
  437. */
  438. public function setFetchMode($mode)
  439. {
  440. switch ($mode) {
  441. case PDO::FETCH_LAZY:
  442. case PDO::FETCH_ASSOC:
  443. case PDO::FETCH_NAMED:
  444. case PDO::FETCH_NUM:
  445. case PDO::FETCH_BOTH:
  446. case PDO::FETCH_OBJ:
  447. $this->fetchMode = $mode;
  448. break;
  449. default:
  450. throw UnexpectedArgumentException('Invalid mode');
  451. }
  452. }
  453. /**
  454. * Set an attribute
  455. *
  456. * @param integer $attribute The attribute to set
  457. * @param mixed $value The attribute's value
  458. *
  459. * @link http://us.php.net/manual/en/pdo.setattribute.php
  460. * @return true False if something failed to set
  461. */
  462. public function setAttribute($attribute, $value)
  463. {
  464. if ($this->pdo->setAttribute($attribute, $value)) {
  465. $this->options[$attribute] = $value;
  466. return true;
  467. }
  468. return false;
  469. }
  470. }
  471. ?>