PageRenderTime 26ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/PDB/Common.php

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