PageRenderTime 39ms CodeModel.GetById 3ms app.highlight 23ms RepoModel.GetById 2ms app.codeStats 1ms

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