* * for PHP 5.0.3 and newer * * @link http://dibi.texy.info/ * @license GNU GENERAL PUBLIC LICENSE version 2 * @package dibi * @category Database * @version 0.8 (Revision: $WCREV$, Date: $WCDATE$) */ /** * This file is part of the "dibi" project (http://dibi.texy.info/) * * Copyright (c) 2005-2007 David Grudl aka -dgx- * * @version $Revision$ $Date$ * @package dibi */ define('DIBI', '0.8 (Revision: $WCREV$, Date: $WCDATE$)'); if (version_compare(PHP_VERSION , '5.0.3', '<')) die('dibi needs PHP 5.0.3 or newer'); // libraries require_once dirname(__FILE__).'/libs/driver.php'; require_once dirname(__FILE__).'/libs/resultset.php'; require_once dirname(__FILE__).'/libs/translator.php'; require_once dirname(__FILE__).'/libs/exception.php'; /** * Interface for user variable, used for generating SQL */ interface IDibiVariable { /** * Format for SQL * * @param object destination DibiDriver * @param string optional modifier * @return string SQL code */ public function toSQL($driver, $modifier = NULL); } /** * Interface for database drivers * * This class is static container class for creating DB objects and * store connections info. * */ class dibi { /** * Column type in relation to PHP native type */ const FIELD_TEXT = 's', // as 'string' FIELD_BINARY = 'S', FIELD_BOOL = 'b', FIELD_INTEGER = 'i', FIELD_FLOAT = 'f', FIELD_DATE = 'd', FIELD_DATETIME = 't', FIELD_UNKNOWN = '?', // special FIELD_COUNTER = 'c'; // counter or autoincrement, is integer /** * Connection registry storage for DibiDriver objects * @var DibiDriver[] */ static private $registry = array(); /** * Current connection * @var DibiDriver */ static private $conn; /** * Last SQL command @see dibi::query() * @var string */ static public $sql; /** * File for logging SQL queries * @var string|NULL */ static public $logFile; /** * Mode parameter used by fopen() * @var string */ static public $logMode = 'a'; /** * To log all queries or error queries (debug mode) * @var bool */ static public $logAll = FALSE; /** * dibi::query() error mode * @var bool */ static public $throwExceptions = FALSE; /** * Substitutions for identifiers * @var array */ static private $substs = array(); /** * Monostate class */ private function __construct() {} /** * Creates a new DibiDriver object and connects it to specified database * * @param array|string connection parameters * @param string connection name * @return void * @throw DibiException */ static public function connect($config, $name=0) { // DSN string if (is_string($config)) parse_str($config, $config); // config['driver'] is required if (empty($config['driver'])) throw new DibiException('Driver is not specified.'); // include dibi driver $className = "Dibi$config[driver]Driver"; if (!class_exists($className)) { include_once dirname(__FILE__) . "/drivers/$config[driver].php"; if (!class_exists($className)) throw new DibiException("Unable to create instance of dibi driver class '$className'."); } // create connection object and store in list /** like $conn = $className::connect($config); */ self::$conn = self::$registry[$name] = new $className($config); if (dibi::$logAll) dibi::log("OK: connected to DB '$config[driver]'"); } /** * Returns TRUE when connection was established * * @return bool */ static public function isConnected() { return (bool) self::$conn; } /** * Retrieve active connection * * @param string connection registy name * @return object DibiDriver object. * @throw DibiException */ static public function getConnection($name=NULL) { if ($name === NULL) { if (!self::$conn) throw new DibiException('Dibi is not connected to database'); return self::$conn; } if (!isset(self::$registry[$name])) throw new DibiException("There is no connection named '$name'."); return self::$registry[$name]; } /** * Change active connection * * @param string connection registy name * @return void * @throw DibiException */ static public function activate($name) { self::$conn = self::getConnection($name); } /** * Generates and executes SQL query - Monostate for DibiDriver::query() * * @param array|mixed one or more arguments * @return int|DibiResult * @throw DibiException */ static public function query($args) { $args = func_get_args(); return self::getConnection()->query($args); } /** * Generates and prints SQL query * * @param array|mixed one or more arguments * @return bool */ static public function test($args) { // receive arguments if (!is_array($args)) $args = func_get_args(); // and generate SQL $trans = new DibiTranslator(self::getConnection()); try { $sql = $trans->translate($args); } catch (DibiException $e) { return FALSE; } if ($sql === FALSE) return FALSE; self::dump($sql); return TRUE; } /** * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query * Monostate for DibiDriver::insertId() * * @return int|bool int on success or FALSE on failure */ static public function insertId() { return self::getConnection()->insertId(); } /** * Gets the number of affected rows * Monostate for DibiDriver::affectedRows() * * @return int number of rows or FALSE on error */ static public function affectedRows() { return self::getConnection()->affectedRows(); } /** * Executes the SQL query - Monostate for DibiDriver::nativeQuery() * * @param string SQL statement. * @return object|bool Result set object or TRUE on success, FALSE on failure */ static public function nativeQuery($sql) { return self::getConnection()->nativeQuery($sql); } static private function dumpHighlight($matches) { if (!empty($matches[1])) // comment return ''.$matches[1].''; if (!empty($matches[2])) // error return ''.$matches[2].''; if (!empty($matches[3])) // most important keywords return ''.$matches[3].''; if (!empty($matches[4])) // other keywords return ''.$matches[4].''; } /** * Prints out a syntax highlighted version of the SQL command * * @param string SQL command * @param bool return or print? * @return void */ static public function dump($sql, $return=FALSE) { static $keywords2 = 'ALL|DISTINCT|AS|ON|INTO|AND|OR|AS'; static $keywords1 = 'SELECT|UPDATE|INSERT|DELETE|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN'; // insert new lines $sql = preg_replace("#\\b(?:$keywords1)\\b#", "\n\$0", $sql); $sql = trim($sql); // reduce spaces $sql = preg_replace('# {2,}#', ' ', $sql); $sql = wordwrap($sql, 100); $sql = htmlSpecialChars($sql); $sql = preg_replace("#\n{2,}#", "\n", $sql); // syntax highlight $sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|\\b($keywords1)\\b|\\b($keywords2)\\b#", array('dibi', 'dumpHighlight'), $sql); $sql = '
' . $sql . "
\n"; // print & return if (!$return) echo $sql; return $sql; } /** * Displays complete result-set as HTML table * * @param object DibiResult * @return void */ static public function dumpResult(DibiResult $res) { echo ''; echo ''; foreach ($res->getFields() as $field) echo ''; echo ''; foreach ($res as $row => $fields) { echo ''; foreach ($fields as $field) { if (is_object($field)) $field = $field->__toString(); echo ''; } echo ''; } echo '
#row' . $field . '
', $row, '', htmlSpecialChars($field), '
'; } /** * Create a new substitution pair for indentifiers * @param string from * @param string to * @return void */ static public function addSubst($expr, $subst) { self::$substs[':'.$expr.':'] = $subst; } /** * Remove substitution pair * @param string from * @return void */ static public function removeSubst($expr) { unset(self::$substs[':'.$expr.':']); } /** * Process substitutions in string * @param string * @return string */ static public function substitute($s) { if (strpos($s, ':') === FALSE) return $s; return strtr($s, self::$substs); } /** * Error logging * EXPERIMENTAL */ static public function log($message) { if (self::$logFile == NULL || self::$logMode == NULL) return; $f = fopen(self::$logFile, self::$logMode); if (!$f) return; flock($f, LOCK_EX); fwrite($f, $message. "\n\n"); fclose($f); } } // class dibi