diff --git a/TODO.txt b/TODO.txt index b7b97e96..65a6da10 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1 +1 @@ -- event, log, profiler +- event, log, profiler diff --git a/dibi/Nette/IDebuggable.php b/dibi/Nette/IDebuggable.php index d4f96da4..2cfc2803 100644 --- a/dibi/Nette/IDebuggable.php +++ b/dibi/Nette/IDebuggable.php @@ -1,41 +1,40 @@ - - * $val = $obj->label; // equivalent to $val = $obj->getLabel(); - * $obj->label = 'Nette'; // equivalent to $obj->setLabel('Nette'); - * - * Property names are case-sensitive, and they are written in the camelCaps - * or PascalCaps. - * - * Event functionality is provided by declaration of property named 'on{Something}' - * Multiple handlers are allowed. - * - * public $onClick; // declaration in class - * $this->onClick[] = 'callback'; // attaching event handler - * if (!empty($this->onClick)) ... // are there any handlers? - * $this->onClick($sender, $arg); // raises the event with arguments - * - * - * Adding method to class (i.e. to all instances) works similar to JavaScript - * prototype property. The syntax for adding a new method is: - * - * function MyClass_prototype_newMethod(MyClass $obj, $arg, ...) { ... } - * $obj = new MyClass; - * $obj->newMethod($x); // equivalent to MyClass_prototype_newMethod($obj, $x); - * - * - * @author David Grudl - * @copyright Copyright (c) 2004, 2008 David Grudl - * @package Nette - * @version $Revision$ $Date$ - */ -abstract class Object -{ - - /** - * Returns the name of the class of this object. - * - * @return string - */ - final public function getClass() - { - return get_class($this); - } - - - - /** - * Access to reflection. - * - * @return ReflectionObject - */ - final public function getReflection() - { - return new ReflectionObject($this); - } - - - - /** - * Call to undefined method. - * - * @param string method name - * @param array arguments - * @return mixed - * @throws ::MemberAccessException - */ - protected function __call($name, $args) - { - $class = get_class($this); - - if ($name === '') { - throw new /*::*/MemberAccessException("Call to class '$class' method without name."); - } - - // event functionality - if (self::hasEvent($class, $name)) { - $list = $this->$name; - if (is_array($list) || $list instanceof Traversable) { - foreach ($list as $handler) { - call_user_func_array($handler, $args); - } - } - return; - } - - // object prototypes support Class__method() - // (or use class Class__method { static function ... } with autoloading?) - $cl = $class; - do { - if (function_exists($nm = $cl . '_prototype_' . $name)) { - array_unshift($args, $this); - return call_user_func_array($nm, $args); - } - } while ($cl = get_parent_class($cl)); - - throw new /*::*/MemberAccessException("Call to undefined method $class::$name()."); - } - - - - /** - * Call to undefined static method. - * - * @param string method name (in lower case!) - * @param array arguments - * @return mixed - * @throws ::MemberAccessException - */ - protected static function __callStatic($name, $args) - { - $class = get_called_class(); - throw new /*::*/MemberAccessException("Call to undefined static method $class::$name()."); - } - - - - /** - * Returns property value. Do not call directly. - * - * @param string property name - * @return mixed property value - * @throws ::MemberAccessException if the property is not defined. - */ - protected function &__get($name) - { - $class = get_class($this); - - if ($name === '') { - throw new /*::*/MemberAccessException("Cannot read an class '$class' property without name."); - } - - // property getter support - $m = 'get' . $name; - if (self::hasAccessor($class, $m)) { - // ampersands: - // - uses &__get() because declaration should be forward compatible (e.g. with Nette::Web::Html) - // - doesn't call &$this->$m because user could bypass property setter by: $x = & $obj->property; $x = 'new value'; - $val = $this->$m(); - return $val; - - } else { - throw new /*::*/MemberAccessException("Cannot read an undeclared property $class::\$$name."); - } - } - - - - /** - * Sets value of a property. Do not call directly. - * - * @param string property name - * @param mixed property value - * @return void - * @throws ::MemberAccessException if the property is not defined or is read-only - */ - protected function __set($name, $value) - { - $class = get_class($this); - - if ($name === '') { - throw new /*::*/MemberAccessException("Cannot assign to an class '$class' property without name."); - } - - // property setter support - if (self::hasAccessor($class, 'get' . $name)) { - $m = 'set' . $name; - if (self::hasAccessor($class, $m)) { - $this->$m($value); - - } else { - throw new /*::*/MemberAccessException("Cannot assign to a read-only property $class::\$$name."); - } - - } else { - throw new /*::*/MemberAccessException("Cannot assign to an undeclared property $class::\$$name."); - } - } - - - - /** - * Is property defined? - * - * @param string property name - * @return bool - */ - protected function __isset($name) - { - return $name !== '' && self::hasAccessor(get_class($this), 'get' . $name); - } - - - - /** - * Access to undeclared property. - * - * @param string property name - * @return void - * @throws ::MemberAccessException - */ - protected function __unset($name) - { - $class = get_class($this); - throw new /*::*/MemberAccessException("Cannot unset an property $class::\$$name."); - } - - - - /** - * Has property an accessor? - * - * @param string class name - * @param string method name - * @return bool - */ - private static function hasAccessor($c, $m) - { - static $cache; - if (!isset($cache[$c])) { - // get_class_methods returns private, protected and public methods of Object (doesn't matter) - // and ONLY PUBLIC methods of descendants (perfect!) - // but returns static methods too (nothing doing...) - // and is much faster than reflection - // (works good since 5.0.4) - $cache[$c] = array_flip(get_class_methods($c)); - } - // case-sensitive checking, capitalize the fourth character - $m[3] = $m[3] & "\xDF"; - return isset($cache[$c][$m]); - } - - - - /** - * Is property an event? - * - * @param string class name - * @param string method name - * @return bool - */ - private static function hasEvent($c, $m) - { - return preg_match('#^on[A-Z]#', $m) && property_exists($c, $m); - } - -} + + * $val = $obj->label; // equivalent to $val = $obj->getLabel(); + * $obj->label = 'Nette'; // equivalent to $obj->setLabel('Nette'); + * + * Property names are case-sensitive, and they are written in the camelCaps + * or PascalCaps. + * + * Event functionality is provided by declaration of property named 'on{Something}' + * Multiple handlers are allowed. + * + * public $onClick; // declaration in class + * $this->onClick[] = 'callback'; // attaching event handler + * if (!empty($this->onClick)) ... // are there any handlers? + * $this->onClick($sender, $arg); // raises the event with arguments + * + * + * Adding method to class (i.e. to all instances) works similar to JavaScript + * prototype property. The syntax for adding a new method is: + * + * function MyClass_prototype_newMethod(MyClass $obj, $arg, ...) { ... } + * $obj = new MyClass; + * $obj->newMethod($x); // equivalent to MyClass_prototype_newMethod($obj, $x); + * + * + * @author David Grudl + * @copyright Copyright (c) 2004, 2008 David Grudl + * @package Nette + */ +abstract class Object +{ + + /** + * Returns the name of the class of this object. + * + * @return string + */ + final public function getClass() + { + return get_class($this); + } + + + + /** + * Access to reflection. + * + * @return ReflectionObject + */ + final public function getReflection() + { + return new ReflectionObject($this); + } + + + + /** + * Call to undefined method. + * + * @param string method name + * @param array arguments + * @return mixed + * @throws ::MemberAccessException + */ + protected function __call($name, $args) + { + $class = get_class($this); + + if ($name === '') { + throw new /*::*/MemberAccessException("Call to class '$class' method without name."); + } + + // event functionality + if (self::hasEvent($class, $name)) { + $list = $this->$name; + if (is_array($list) || $list instanceof Traversable) { + foreach ($list as $handler) { + call_user_func_array($handler, $args); + } + } + return; + } + + // object prototypes support Class__method() + // (or use class Class__method { static function ... } with autoloading?) + $cl = $class; + do { + if (function_exists($nm = $cl . '_prototype_' . $name)) { + array_unshift($args, $this); + return call_user_func_array($nm, $args); + } + } while ($cl = get_parent_class($cl)); + + throw new /*::*/MemberAccessException("Call to undefined method $class::$name()."); + } + + + + /** + * Call to undefined static method. + * + * @param string method name (in lower case!) + * @param array arguments + * @return mixed + * @throws ::MemberAccessException + */ + protected static function __callStatic($name, $args) + { + $class = get_called_class(); + throw new /*::*/MemberAccessException("Call to undefined static method $class::$name()."); + } + + + + /** + * Returns property value. Do not call directly. + * + * @param string property name + * @return mixed property value + * @throws ::MemberAccessException if the property is not defined. + */ + protected function &__get($name) + { + $class = get_class($this); + + if ($name === '') { + throw new /*::*/MemberAccessException("Cannot read an class '$class' property without name."); + } + + // property getter support + $m = 'get' . $name; + if (self::hasAccessor($class, $m)) { + // ampersands: + // - uses &__get() because declaration should be forward compatible (e.g. with Nette::Web::Html) + // - doesn't call &$this->$m because user could bypass property setter by: $x = & $obj->property; $x = 'new value'; + $val = $this->$m(); + return $val; + + } else { + throw new /*::*/MemberAccessException("Cannot read an undeclared property $class::\$$name."); + } + } + + + + /** + * Sets value of a property. Do not call directly. + * + * @param string property name + * @param mixed property value + * @return void + * @throws ::MemberAccessException if the property is not defined or is read-only + */ + protected function __set($name, $value) + { + $class = get_class($this); + + if ($name === '') { + throw new /*::*/MemberAccessException("Cannot assign to an class '$class' property without name."); + } + + // property setter support + if (self::hasAccessor($class, 'get' . $name)) { + $m = 'set' . $name; + if (self::hasAccessor($class, $m)) { + $this->$m($value); + + } else { + throw new /*::*/MemberAccessException("Cannot assign to a read-only property $class::\$$name."); + } + + } else { + throw new /*::*/MemberAccessException("Cannot assign to an undeclared property $class::\$$name."); + } + } + + + + /** + * Is property defined? + * + * @param string property name + * @return bool + */ + protected function __isset($name) + { + return $name !== '' && self::hasAccessor(get_class($this), 'get' . $name); + } + + + + /** + * Access to undeclared property. + * + * @param string property name + * @return void + * @throws ::MemberAccessException + */ + protected function __unset($name) + { + $class = get_class($this); + throw new /*::*/MemberAccessException("Cannot unset an property $class::\$$name."); + } + + + + /** + * Has property an accessor? + * + * @param string class name + * @param string method name + * @return bool + */ + private static function hasAccessor($c, $m) + { + static $cache; + if (!isset($cache[$c])) { + // get_class_methods returns private, protected and public methods of Object (doesn't matter) + // and ONLY PUBLIC methods of descendants (perfect!) + // but returns static methods too (nothing doing...) + // and is much faster than reflection + // (works good since 5.0.4) + $cache[$c] = array_flip(get_class_methods($c)); + } + // case-sensitive checking, capitalize the fourth character + $m[3] = $m[3] & "\xDF"; + return isset($cache[$c][$m]); + } + + + + /** + * Is property an event? + * + * @param string class name + * @param string method name + * @return bool + */ + private static function hasEvent($c, $m) + { + return preg_match('#^on[A-Z]#', $m) && property_exists($c, $m); + } + +} diff --git a/dibi/dibi.php b/dibi/dibi.php index a1a74e00..4cf8c2a9 100644 --- a/dibi/dibi.php +++ b/dibi/dibi.php @@ -1,796 +1,793 @@ -disconnect(); - } - - - - /** - * Returns TRUE when connection was established. - * - * @return bool - */ - public static function isConnected() - { - return (self::$connection !== NULL) && self::$connection->isConnected(); - } - - - - /** - * Retrieve active connection. - * - * @param string connection registy name - * @return object DibiConnection object. - * @throws DibiException - */ - public static function getConnection($name = NULL) - { - if ($name === NULL) { - if (self::$connection === NULL) { - throw new DibiException('Dibi is not connected to database.'); - } - - return self::$connection; - } - - 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 - * @throws DibiException - */ - public static function activate($name) - { - self::$connection = self::getConnection($name); - } - - - - /********************* monostate for active connection ****************d*g**/ - - - - /** - * Generates and executes SQL query - Monostate for DibiConnection::query(). - * - * @param array|mixed one or more arguments - * @return DibiResult|NULL result set object (if any) - * @throws DibiException - */ - public static function query($args) - { - $args = func_get_args(); - return self::getConnection()->query($args); - } - - - - /** - * Executes the SQL query - Monostate for DibiConnection::nativeQuery(). - * - * @param string SQL statement. - * @return DibiResult|NULL result set object (if any) - */ - public static function nativeQuery($sql) - { - return self::getConnection()->nativeQuery($sql); - } - - - - /** - * Generates and prints SQL query - Monostate for DibiConnection::test(). - * - * @param array|mixed one or more arguments - * @return bool - */ - public static function test($args) - { - $args = func_get_args(); - return self::getConnection()->test($args); - } - - - - /** - * Executes SQL query and fetch result - Monostate for DibiConnection::query() & fetch(). - * - * @param array|mixed one or more arguments - * @return array - * @throws DibiException - */ - public static function fetch($args) - { - $args = func_get_args(); - return self::getConnection()->query($args)->fetch(); - } - - - - /** - * Executes SQL query and fetch results - Monostate for DibiConnection::query() & fetchAll(). - * - * @param array|mixed one or more arguments - * @return array - * @throws DibiException - */ - public static function fetchAll($args) - { - $args = func_get_args(); - return self::getConnection()->query($args)->fetchAll(); - } - - - - /** - * Executes SQL query and fetch first column - Monostate for DibiConnection::query() & fetchSingle(). - * - * @param array|mixed one or more arguments - * @return string - * @throws DibiException - */ - public static function fetchSingle($args) - { - $args = func_get_args(); - return self::getConnection()->query($args)->fetchSingle(); - } - - - - /** - * Gets the number of affected rows. - * Monostate for DibiConnection::affectedRows() - * - * @return int number of rows - * @throws DibiException - */ - public static function affectedRows() - { - return self::getConnection()->affectedRows(); - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * Monostate for DibiConnection::insertId() - * - * @param string optional sequence name - * @return int - * @throws DibiException - */ - public static function insertId($sequence=NULL) - { - return self::getConnection()->insertId($sequence); - } - - - - /** - * Begins a transaction - Monostate for DibiConnection::begin(). - * @return void - * @throws DibiException - */ - public static function begin() - { - self::getConnection()->begin(); - } - - - - /** - * Commits statements in a transaction - Monostate for DibiConnection::commit(). - * @return void - * @throws DibiException - */ - public static function commit() - { - self::getConnection()->commit(); - } - - - - /** - * Rollback changes in a transaction - Monostate for DibiConnection::rollback(). - * @return void - * @throws DibiException - */ - public static function rollback() - { - self::getConnection()->rollback(); - } - - - - /** - * Import SQL dump from file - extreme fast! - * - * @param string filename - * @return int count of sql commands - */ - public static function loadFile($file) - { - return self::getConnection()->loadFile($file); - } - - - - /** - * Replacement for majority of dibi::methods() in future. - */ - protected static function __callStatic($name, $args) - { - //if ($name = 'select', 'update', ...') { - // return self::command()->$name($args); - //} - return call_user_func_array(array(self::getConnection(), $name), $args); - } - - - - /********************* fluent SQL builders ****************d*g**/ - - - - /** - * @return DibiFluent - */ - public static function command() - { - return new DibiFluent(self::getConnection()); - } - - - - /** - * @param string column name - * @return DibiFluent - */ - public static function select($args) - { - $args = func_get_args(); - return self::command()->__call('select', $args); - } - - - - /** - * @param string table - * @param array - * @return DibiFluent - */ - public static function update($table, array $args) - { - return self::command()->update('%n', $table)->set($args); - } - - - - /** - * @param string table - * @param array - * @return DibiFluent - */ - public static function insert($table, array $args) - { - return self::command()->insert() - ->into('%n', $table, '(%n)', array_keys($args))->values('%l', array_values($args)); - } - - - - /** - * @param string table - * @return DibiFluent - */ - public static function delete($table) - { - return self::command()->delete()->from('%n', $table); - } - - - - /********************* data types ****************d*g**/ - - - - /** - * Pseudotype for timestamp representation. - * - * @param mixed datetime - * @return DibiVariable - */ - public static function datetime($time = NULL) - { - if ($time === NULL) { - $time = time(); // current time - } elseif (is_string($time)) { - $time = strtotime($time); // try convert to timestamp - } else { - $time = (int) $time; - } - return new DibiVariable($time, dibi::FIELD_DATETIME); - } - - - - /** - * Pseudotype for date representation. - * - * @param mixed date - * @return DibiVariable - */ - public static function date($date = NULL) - { - $var = self::datetime($date); - $var->modifier = dibi::FIELD_DATE; - return $var; - } - - - - /********************* substitutions ****************d*g**/ - - - - /** - * Create a new substitution pair for indentifiers. - * - * @param string from - * @param string to - * @return void - */ - public static function addSubst($expr, $subst) - { - self::$substs[$expr] = $subst; - } - - - - /** - * Sets substitution fallback handler. - * - * @param callback - * @return void - */ - public static function setSubstFallback($callback) - { - if (!is_callable($callback)) { - throw new InvalidArgumentException("Invalid callback."); - } - - self::$substFallBack = $callback; - } - - - - /** - * Remove substitution pair. - * - * @param mixed from or TRUE - * @return void - */ - public static function removeSubst($expr) - { - if ($expr === TRUE) { - self::$substs = array(); - } else { - unset(self::$substs[':'.$expr.':']); - } - } - - - - /** - * Provides substitution. - * - * @param string - * @return string - */ - public static function substitute($value) - { - if (strpos($value, ':') === FALSE) { - return $value; - - } else { - return preg_replace_callback('#:(.*):#U', array('dibi', 'subCb'), $value); - } - } - - - - /** - * Substitution callback. - * - * @param array - * @return string - */ - private static function subCb($m) - { - $m = $m[1]; - if (isset(self::$substs[$m])) { - return self::$substs[$m]; - - } elseif (self::$substFallBack) { - return self::$substs[$m] = call_user_func(self::$substFallBack, $m); - - } else { - return $m; - } - } - - - - /********************* event handling ****************d*g**/ - - - - /** - * Add new event handler. - * - * @param callback - * @return void - * @throws InvalidArgumentException - */ - public static function addHandler($callback) - { - if (!is_callable($callback)) { - throw new InvalidArgumentException("Invalid callback."); - } - - self::$handlers[] = $callback; - } - - - - /** - * Event notification (events: exception, connected, beforeQuery, afterQuery, begin, commit, rollback). - * - * @param DibiConnection - * @param string event name - * @param mixed - * @return void - */ - public static function notify(DibiConnection $connection = NULL, $event, $arg = NULL) - { - foreach (self::$handlers as $handler) { - call_user_func($handler, $connection, $event, $arg); - } - } - - - - /** - * Enable profiler & logger. - * - * @param string filename - * @param bool log all queries? - * @return DibiProfiler - */ - public static function startLogger($file, $logQueries = FALSE) - { - require_once dirname(__FILE__) . '/libs/DibiLogger.php'; - - $logger = new DibiLogger($file); - $logger->logQueries = $logQueries; - self::addHandler(array($logger, 'handler')); - return $logger; - } - - - - /********************* misc tools ****************d*g**/ - - - - /** - * Prints out a syntax highlighted version of the SQL command or DibiResult. - * - * @param string|DibiResult - * @param bool return output instead of printing it? - * @return string - */ - public static function dump($sql = NULL, $return = FALSE) - { - ob_start(); - if ($sql instanceof DibiResult) { - $sql->dump(); - - } else { - if ($sql === NULL) $sql = self::$sql; - - static $keywords1 = 'SELECT|UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE'; - static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|TRUE|FALSE'; - - // insert new lines - $sql = ' ' . $sql; - $sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql); - - // reduce spaces - $sql = preg_replace('#[ \t]{2,}#', " ", $sql); - - $sql = wordwrap($sql, 100); - $sql = htmlSpecialChars($sql); - $sql = preg_replace("#\n{2,}#", "\n", $sql); - - // syntax highlight - $sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", array('dibi', 'highlightCallback'), $sql); - $sql = trim($sql); - echo '
', $sql, "
\n"; - } - - if ($return) { - return ob_get_clean(); - } else { - ob_end_flush(); - } - } - - - - private static function highlightCallback($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] . ''; - } - - - - /** - * Returns brief descriptions. - * @return string - * @return array - */ - public static function getColophon($sender = NULL) - { - $arr = array( - 'Number of SQL queries: ' . dibi::$numOfQueries - . (dibi::$totalTime === NULL ? '' : ', elapsed time: ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms'), - ); - if ($sender === 'bluescreen') { - $arr[] = 'dibi version ' . dibi::VERSION; - } - return $arr; - } - -} +disconnect(); + } + + + + /** + * Returns TRUE when connection was established. + * + * @return bool + */ + public static function isConnected() + { + return (self::$connection !== NULL) && self::$connection->isConnected(); + } + + + + /** + * Retrieve active connection. + * + * @param string connection registy name + * @return object DibiConnection object. + * @throws DibiException + */ + public static function getConnection($name = NULL) + { + if ($name === NULL) { + if (self::$connection === NULL) { + throw new DibiException('Dibi is not connected to database.'); + } + + return self::$connection; + } + + 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 + * @throws DibiException + */ + public static function activate($name) + { + self::$connection = self::getConnection($name); + } + + + + /********************* monostate for active connection ****************d*g**/ + + + + /** + * Generates and executes SQL query - Monostate for DibiConnection::query(). + * + * @param array|mixed one or more arguments + * @return DibiResult|NULL result set object (if any) + * @throws DibiException + */ + public static function query($args) + { + $args = func_get_args(); + return self::getConnection()->query($args); + } + + + + /** + * Executes the SQL query - Monostate for DibiConnection::nativeQuery(). + * + * @param string SQL statement. + * @return DibiResult|NULL result set object (if any) + */ + public static function nativeQuery($sql) + { + return self::getConnection()->nativeQuery($sql); + } + + + + /** + * Generates and prints SQL query - Monostate for DibiConnection::test(). + * + * @param array|mixed one or more arguments + * @return bool + */ + public static function test($args) + { + $args = func_get_args(); + return self::getConnection()->test($args); + } + + + + /** + * Executes SQL query and fetch result - Monostate for DibiConnection::query() & fetch(). + * + * @param array|mixed one or more arguments + * @return array + * @throws DibiException + */ + public static function fetch($args) + { + $args = func_get_args(); + return self::getConnection()->query($args)->fetch(); + } + + + + /** + * Executes SQL query and fetch results - Monostate for DibiConnection::query() & fetchAll(). + * + * @param array|mixed one or more arguments + * @return array + * @throws DibiException + */ + public static function fetchAll($args) + { + $args = func_get_args(); + return self::getConnection()->query($args)->fetchAll(); + } + + + + /** + * Executes SQL query and fetch first column - Monostate for DibiConnection::query() & fetchSingle(). + * + * @param array|mixed one or more arguments + * @return string + * @throws DibiException + */ + public static function fetchSingle($args) + { + $args = func_get_args(); + return self::getConnection()->query($args)->fetchSingle(); + } + + + + /** + * Gets the number of affected rows. + * Monostate for DibiConnection::affectedRows() + * + * @return int number of rows + * @throws DibiException + */ + public static function affectedRows() + { + return self::getConnection()->affectedRows(); + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * Monostate for DibiConnection::insertId() + * + * @param string optional sequence name + * @return int + * @throws DibiException + */ + public static function insertId($sequence=NULL) + { + return self::getConnection()->insertId($sequence); + } + + + + /** + * Begins a transaction - Monostate for DibiConnection::begin(). + * @return void + * @throws DibiException + */ + public static function begin() + { + self::getConnection()->begin(); + } + + + + /** + * Commits statements in a transaction - Monostate for DibiConnection::commit(). + * @return void + * @throws DibiException + */ + public static function commit() + { + self::getConnection()->commit(); + } + + + + /** + * Rollback changes in a transaction - Monostate for DibiConnection::rollback(). + * @return void + * @throws DibiException + */ + public static function rollback() + { + self::getConnection()->rollback(); + } + + + + /** + * Import SQL dump from file - extreme fast! + * + * @param string filename + * @return int count of sql commands + */ + public static function loadFile($file) + { + return self::getConnection()->loadFile($file); + } + + + + /** + * Replacement for majority of dibi::methods() in future. + */ + protected static function __callStatic($name, $args) + { + //if ($name = 'select', 'update', ...') { + // return self::command()->$name($args); + //} + return call_user_func_array(array(self::getConnection(), $name), $args); + } + + + + /********************* fluent SQL builders ****************d*g**/ + + + + /** + * @return DibiFluent + */ + public static function command() + { + return new DibiFluent(self::getConnection()); + } + + + + /** + * @param string column name + * @return DibiFluent + */ + public static function select($args) + { + $args = func_get_args(); + return self::command()->__call('select', $args); + } + + + + /** + * @param string table + * @param array + * @return DibiFluent + */ + public static function update($table, array $args) + { + return self::command()->update('%n', $table)->set($args); + } + + + + /** + * @param string table + * @param array + * @return DibiFluent + */ + public static function insert($table, array $args) + { + return self::command()->insert() + ->into('%n', $table, '(%n)', array_keys($args))->values('%l', array_values($args)); + } + + + + /** + * @param string table + * @return DibiFluent + */ + public static function delete($table) + { + return self::command()->delete()->from('%n', $table); + } + + + + /********************* data types ****************d*g**/ + + + + /** + * Pseudotype for timestamp representation. + * + * @param mixed datetime + * @return DibiVariable + */ + public static function datetime($time = NULL) + { + if ($time === NULL) { + $time = time(); // current time + } elseif (is_string($time)) { + $time = strtotime($time); // try convert to timestamp + } else { + $time = (int) $time; + } + return new DibiVariable($time, dibi::FIELD_DATETIME); + } + + + + /** + * Pseudotype for date representation. + * + * @param mixed date + * @return DibiVariable + */ + public static function date($date = NULL) + { + $var = self::datetime($date); + $var->modifier = dibi::FIELD_DATE; + return $var; + } + + + + /********************* substitutions ****************d*g**/ + + + + /** + * Create a new substitution pair for indentifiers. + * + * @param string from + * @param string to + * @return void + */ + public static function addSubst($expr, $subst) + { + self::$substs[$expr] = $subst; + } + + + + /** + * Sets substitution fallback handler. + * + * @param callback + * @return void + */ + public static function setSubstFallback($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException("Invalid callback."); + } + + self::$substFallBack = $callback; + } + + + + /** + * Remove substitution pair. + * + * @param mixed from or TRUE + * @return void + */ + public static function removeSubst($expr) + { + if ($expr === TRUE) { + self::$substs = array(); + } else { + unset(self::$substs[':'.$expr.':']); + } + } + + + + /** + * Provides substitution. + * + * @param string + * @return string + */ + public static function substitute($value) + { + if (strpos($value, ':') === FALSE) { + return $value; + + } else { + return preg_replace_callback('#:(.*):#U', array('dibi', 'subCb'), $value); + } + } + + + + /** + * Substitution callback. + * + * @param array + * @return string + */ + private static function subCb($m) + { + $m = $m[1]; + if (isset(self::$substs[$m])) { + return self::$substs[$m]; + + } elseif (self::$substFallBack) { + return self::$substs[$m] = call_user_func(self::$substFallBack, $m); + + } else { + return $m; + } + } + + + + /********************* event handling ****************d*g**/ + + + + /** + * Add new event handler. + * + * @param callback + * @return void + * @throws InvalidArgumentException + */ + public static function addHandler($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException("Invalid callback."); + } + + self::$handlers[] = $callback; + } + + + + /** + * Event notification (events: exception, connected, beforeQuery, afterQuery, begin, commit, rollback). + * + * @param DibiConnection + * @param string event name + * @param mixed + * @return void + */ + public static function notify(DibiConnection $connection = NULL, $event, $arg = NULL) + { + foreach (self::$handlers as $handler) { + call_user_func($handler, $connection, $event, $arg); + } + } + + + + /** + * Enable profiler & logger. + * + * @param string filename + * @param bool log all queries? + * @return DibiProfiler + */ + public static function startLogger($file, $logQueries = FALSE) + { + require_once dirname(__FILE__) . '/libs/DibiLogger.php'; + + $logger = new DibiLogger($file); + $logger->logQueries = $logQueries; + self::addHandler(array($logger, 'handler')); + return $logger; + } + + + + /********************* misc tools ****************d*g**/ + + + + /** + * Prints out a syntax highlighted version of the SQL command or DibiResult. + * + * @param string|DibiResult + * @param bool return output instead of printing it? + * @return string + */ + public static function dump($sql = NULL, $return = FALSE) + { + ob_start(); + if ($sql instanceof DibiResult) { + $sql->dump(); + + } else { + if ($sql === NULL) $sql = self::$sql; + + static $keywords1 = 'SELECT|UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE'; + static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|TRUE|FALSE'; + + // insert new lines + $sql = ' ' . $sql; + $sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql); + + // reduce spaces + $sql = preg_replace('#[ \t]{2,}#', " ", $sql); + + $sql = wordwrap($sql, 100); + $sql = htmlSpecialChars($sql); + $sql = preg_replace("#\n{2,}#", "\n", $sql); + + // syntax highlight + $sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", array('dibi', 'highlightCallback'), $sql); + $sql = trim($sql); + echo '
', $sql, "
\n"; + } + + if ($return) { + return ob_get_clean(); + } else { + ob_end_flush(); + } + } + + + + private static function highlightCallback($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] . ''; + } + + + + /** + * Returns brief descriptions. + * @return string + * @return array + */ + public static function getColophon($sender = NULL) + { + $arr = array( + 'Number of SQL queries: ' . dibi::$numOfQueries + . (dibi::$totalTime === NULL ? '' : ', elapsed time: ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms'), + ); + if ($sender === 'bluescreen') { + $arr[] = 'dibi ' . dibi::VERSION . ' (revision ' . dibi::REVISION . ')'; + } + return $arr; + } + +} diff --git a/dibi/drivers/mssql.php b/dibi/drivers/mssql.php index 63fe73ca..b3fce4e2 100644 --- a/dibi/drivers/mssql.php +++ b/dibi/drivers/mssql.php @@ -1,364 +1,364 @@ -connection = @mssql_connect($config['host'], $config['username'], $config['password'], TRUE); // intentionally @ - } else { - $this->connection = @mssql_pconnect($config['host'], $config['username'], $config['password']); // intentionally @ - } - - if (!is_resource($this->connection)) { - throw new DibiDriverException("Can't connect to DB."); - } - - if (isset($config['database']) && !@mssql_select_db($config['database'], $this->connection)) { // intentionally @ - throw new DibiDriverException("Can't select DB '$config[database]'."); - } - } - - - - /** - * Disconnects from a database. - * - * @return void - */ - public function disconnect() - { - mssql_close($this->connection); - } - - - - /** - * Executes the SQL query. - * - * @param string SQL statement. - * @return IDibiDriver|NULL - * @throws DibiDriverException - */ - public function query($sql) - { - $this->resultSet = @mssql_query($sql, $this->connection); // intentionally @ - - if ($this->resultSet === FALSE) { - throw new DibiDriverException('Query error', 0, $sql); - } - - return is_resource($this->resultSet) ? clone $this : NULL; - } - - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * - * @return int|FALSE number of rows or FALSE on error - */ - public function affectedRows() - { - return mssql_rows_affected($this->connection); - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * - * @return int|FALSE int on success or FALSE on failure - */ - public function insertId($sequence) - { - throw new NotSupportedException('MS SQL does not support autoincrementing.'); - } - - - - /** - * Begins a transaction (if supported). - * @return void - * @throws DibiDriverException - */ - public function begin() - { - $this->query('BEGIN TRANSACTION'); - } - - - - /** - * Commits statements in a transaction. - * @return void - * @throws DibiDriverException - */ - public function commit() - { - $this->query('COMMIT'); - } - - - - /** - * Rollback changes in a transaction. - * @return void - * @throws DibiDriverException - */ - public function rollback() - { - $this->query('ROLLBACK'); - } - - - - /** - * Encodes data for use in an SQL statement. - * - * @param string value - * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) - * @return string encoded value - * @throws InvalidArgumentException - */ - public function escape($value, $type) - { - switch ($type) { - case dibi::FIELD_TEXT: - case dibi::FIELD_BINARY: - return "'" . str_replace("'", "''", $value) . "'"; - - case dibi::IDENTIFIER: - return '[' . str_replace('.', '].[', $value) . ']'; - - case dibi::FIELD_BOOL: - return $value ? -1 : 0; - - case dibi::FIELD_DATE: - return date("'Y-m-d'", $value); - - case dibi::FIELD_DATETIME: - return date("'Y-m-d H:i:s'", $value); - - default: - throw new InvalidArgumentException('Unsupported type.'); - } - } - - - - /** - * Decodes data from result set. - * - * @param string value - * @param string type (dibi::FIELD_BINARY) - * @return string decoded value - * @throws InvalidArgumentException - */ - public function unescape($value, $type) - { - throw new InvalidArgumentException('Unsupported type.'); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - * - * @param string &$sql The SQL query that will be modified. - * @param int $limit - * @param int $offset - * @return void - */ - public function applyLimit(&$sql, $limit, $offset) - { - // offset suppot is missing... - if ($limit >= 0) { - $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')'; - } - - if ($offset) { - throw new NotImplementedException('Offset is not implemented.'); - } - } - - - - /** - * Returns the number of rows in a result set. - * - * @return int - */ - public function rowCount() - { - return mssql_num_rows($this->resultSet); - } - - - - /** - * Fetches the row at current position and moves the internal cursor to the next position. - * internal usage only - * - * @param bool TRUE for associative array, FALSE for numeric - * @return array array on success, nonarray if no next record - */ - public function fetch($type) - { - return mssql_fetch_array($this->resultSet, $type ? MSSQL_ASSOC : MSSQL_NUM); - } - - - - /** - * Moves cursor position without fetching row. - * - * @param int the 0-based cursor pos to seek to - * @return boolean TRUE on success, FALSE if unable to seek to specified record - * @throws DibiException - */ - public function seek($row) - { - return mssql_data_seek($this->resultSet, $row); - } - - - - /** - * Frees the resources allocated for this result set. - * - * @return void - */ - public function free() - { - mssql_free_result($this->resultSet); - $this->resultSet = NULL; - } - - - - /** - * Returns metadata for all columns in a result set. - * - * @return array - */ - public function getColumnsMeta() - { - $count = mssql_num_fields($this->resultSet); - $meta = array(); - for ($i = 0; $i < $count; $i++) { - // items 'name' and 'table' are required - $info = (array) mssql_fetch_field($this->resultSet, $i); - $info['table'] = $info['column_source']; - $meta[] = $info; - } - return $meta; - } - - - - /** - * Returns the connection resource. - * - * @return mixed - */ - public function getResource() - { - return $this->connection; - } - - - - /** - * Returns the result set resource. - * - * @return mixed - */ - public function getResultResource() - { - return $this->resultSet; - } - - - - /** - * Gets a information of the current database. - * - * @return DibiReflection - */ - function getDibiReflection() - {} - -} +connection = @mssql_connect($config['host'], $config['username'], $config['password'], TRUE); // intentionally @ + } else { + $this->connection = @mssql_pconnect($config['host'], $config['username'], $config['password']); // intentionally @ + } + + if (!is_resource($this->connection)) { + throw new DibiDriverException("Can't connect to DB."); + } + + if (isset($config['database']) && !@mssql_select_db($config['database'], $this->connection)) { // intentionally @ + throw new DibiDriverException("Can't select DB '$config[database]'."); + } + } + + + + /** + * Disconnects from a database. + * + * @return void + */ + public function disconnect() + { + mssql_close($this->connection); + } + + + + /** + * Executes the SQL query. + * + * @param string SQL statement. + * @return IDibiDriver|NULL + * @throws DibiDriverException + */ + public function query($sql) + { + $this->resultSet = @mssql_query($sql, $this->connection); // intentionally @ + + if ($this->resultSet === FALSE) { + throw new DibiDriverException('Query error', 0, $sql); + } + + return is_resource($this->resultSet) ? clone $this : NULL; + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * + * @return int|FALSE number of rows or FALSE on error + */ + public function affectedRows() + { + return mssql_rows_affected($this->connection); + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * + * @return int|FALSE int on success or FALSE on failure + */ + public function insertId($sequence) + { + throw new NotSupportedException('MS SQL does not support autoincrementing.'); + } + + + + /** + * Begins a transaction (if supported). + * @return void + * @throws DibiDriverException + */ + public function begin() + { + $this->query('BEGIN TRANSACTION'); + } + + + + /** + * Commits statements in a transaction. + * @return void + * @throws DibiDriverException + */ + public function commit() + { + $this->query('COMMIT'); + } + + + + /** + * Rollback changes in a transaction. + * @return void + * @throws DibiDriverException + */ + public function rollback() + { + $this->query('ROLLBACK'); + } + + + + /** + * Encodes data for use in an SQL statement. + * + * @param string value + * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) + * @return string encoded value + * @throws InvalidArgumentException + */ + public function escape($value, $type) + { + switch ($type) { + case dibi::FIELD_TEXT: + case dibi::FIELD_BINARY: + return "'" . str_replace("'", "''", $value) . "'"; + + case dibi::IDENTIFIER: + return '[' . str_replace('.', '].[', $value) . ']'; + + case dibi::FIELD_BOOL: + return $value ? -1 : 0; + + case dibi::FIELD_DATE: + return date("'Y-m-d'", $value); + + case dibi::FIELD_DATETIME: + return date("'Y-m-d H:i:s'", $value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Decodes data from result set. + * + * @param string value + * @param string type (dibi::FIELD_BINARY) + * @return string decoded value + * @throws InvalidArgumentException + */ + public function unescape($value, $type) + { + throw new InvalidArgumentException('Unsupported type.'); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + // offset suppot is missing... + if ($limit >= 0) { + $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')'; + } + + if ($offset) { + throw new NotImplementedException('Offset is not implemented.'); + } + } + + + + /** + * Returns the number of rows in a result set. + * + * @return int + */ + public function rowCount() + { + return mssql_num_rows($this->resultSet); + } + + + + /** + * Fetches the row at current position and moves the internal cursor to the next position. + * internal usage only + * + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record + */ + public function fetch($type) + { + return mssql_fetch_array($this->resultSet, $type ? MSSQL_ASSOC : MSSQL_NUM); + } + + + + /** + * Moves cursor position without fetching row. + * + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + public function seek($row) + { + return mssql_data_seek($this->resultSet, $row); + } + + + + /** + * Frees the resources allocated for this result set. + * + * @return void + */ + public function free() + { + mssql_free_result($this->resultSet); + $this->resultSet = NULL; + } + + + + /** + * Returns metadata for all columns in a result set. + * + * @return array + */ + public function getColumnsMeta() + { + $count = mssql_num_fields($this->resultSet); + $meta = array(); + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $info = (array) mssql_fetch_field($this->resultSet, $i); + $info['table'] = $info['column_source']; + $meta[] = $info; + } + return $meta; + } + + + + /** + * Returns the connection resource. + * + * @return mixed + */ + public function getResource() + { + return $this->connection; + } + + + + /** + * Returns the result set resource. + * + * @return mixed + */ + public function getResultResource() + { + return $this->resultSet; + } + + + + /** + * Gets a information of the current database. + * + * @return DibiReflection + */ + function getDibiReflection() + {} + +} diff --git a/dibi/drivers/mysql.php b/dibi/drivers/mysql.php index d87112ee..2a3fbf39 100644 --- a/dibi/drivers/mysql.php +++ b/dibi/drivers/mysql.php @@ -1,432 +1,432 @@ -connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['options']); // intentionally @ - } else { - $this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['options']); // intentionally @ - } - - if (!is_resource($this->connection)) { - throw new DibiDriverException(mysql_error(), mysql_errno()); - } - - if (isset($config['charset'])) { - $ok = FALSE; - if (function_exists('mysql_set_charset')) { - // affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.2.3) - $ok = @mysql_set_charset($config['charset'], $this->connection); // intentionally @ - } - if (!$ok) $ok = @mysql_query("SET NAMES '$config[charset]'", $this->connection); // intentionally @ - if (!$ok) $this->throwException(); - } - - if (isset($config['database'])) { - @mysql_select_db($config['database'], $this->connection) or $this->throwException(); // intentionally @ - } - - if (isset($config['sqlmode'])) { - if (!@mysql_query("SET sql_mode='$config[sqlmode]'", $this->connection)) $this->throwException(); // intentionally @ - } - - $this->buffered = empty($config['unbuffered']); - } - - - - /** - * Disconnects from a database. - * - * @return void - */ - public function disconnect() - { - mysql_close($this->connection); - } - - - - /** - * Executes the SQL query. - * - * @param string SQL statement. - * @return IDibiDriver|NULL - * @throws DibiDriverException - */ - public function query($sql) - { - if ($this->buffered) { - $this->resultSet = @mysql_query($sql, $this->connection); // intentionally @ - } else { - $this->resultSet = @mysql_unbuffered_query($sql, $this->connection); // intentionally @ - } - - if (mysql_errno($this->connection)) { - $this->throwException($sql); - } - - return is_resource($this->resultSet) ? clone $this : NULL; - } - - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * - * @return int|FALSE number of rows or FALSE on error - */ - public function affectedRows() - { - return mysql_affected_rows($this->connection); - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * - * @return int|FALSE int on success or FALSE on failure - */ - public function insertId($sequence) - { - return mysql_insert_id($this->connection); - } - - - - /** - * Begins a transaction (if supported). - * @return void - * @throws DibiDriverException - */ - public function begin() - { - $this->query('START TRANSACTION'); - } - - - - /** - * Commits statements in a transaction. - * @return void - * @throws DibiDriverException - */ - public function commit() - { - $this->query('COMMIT'); - } - - - - /** - * Rollback changes in a transaction. - * @return void - * @throws DibiDriverException - */ - public function rollback() - { - $this->query('ROLLBACK'); - } - - - - /** - * Encodes data for use in an SQL statement. - * - * @param string value - * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) - * @return string encoded value - * @throws InvalidArgumentException - */ - public function escape($value, $type) - { - switch ($type) { - case dibi::FIELD_TEXT: - case dibi::FIELD_BINARY: - return "'" . mysql_real_escape_string($value, $this->connection) . "'"; - - case dibi::IDENTIFIER: - return '`' . str_replace('.', '`.`', $value) . '`'; - - case dibi::FIELD_BOOL: - return $value ? 1 : 0; - - case dibi::FIELD_DATE: - return date("'Y-m-d'", $value); - - case dibi::FIELD_DATETIME: - return date("'Y-m-d H:i:s'", $value); - - default: - throw new InvalidArgumentException('Unsupported type.'); - } - } - - - - /** - * Decodes data from result set. - * - * @param string value - * @param string type (dibi::FIELD_BINARY) - * @return string decoded value - * @throws InvalidArgumentException - */ - public function unescape($value, $type) - { - throw new InvalidArgumentException('Unsupported type.'); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - * - * @param string &$sql The SQL query that will be modified. - * @param int $limit - * @param int $offset - * @return void - */ - public function applyLimit(&$sql, $limit, $offset) - { - if ($limit < 0 && $offset < 1) return; - - // see http://dev.mysql.com/doc/refman/5.0/en/select.html - $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit) - . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); - } - - - - /** - * Returns the number of rows in a result set. - * - * @return int - */ - public function rowCount() - { - if (!$this->buffered) { - throw new DibiDriverException('Row count is not available for unbuffered queries.'); - } - return mysql_num_rows($this->resultSet); - } - - - - /** - * Fetches the row at current position and moves the internal cursor to the next position. - * internal usage only - * - * @param bool TRUE for associative array, FALSE for numeric - * @return array array on success, nonarray if no next record - */ - public function fetch($type) - { - return mysql_fetch_array($this->resultSet, $type ? MYSQL_ASSOC : MYSQL_NUM); - } - - - - /** - * Moves cursor position without fetching row. - * - * @param int the 0-based cursor pos to seek to - * @return boolean TRUE on success, FALSE if unable to seek to specified record - * @throws DibiException - */ - public function seek($row) - { - if (!$this->buffered) { - throw new DibiDriverException('Cannot seek an unbuffered result set.'); - } - - return mysql_data_seek($this->resultSet, $row); - } - - - - /** - * Frees the resources allocated for this result set. - * - * @return void - */ - public function free() - { - mysql_free_result($this->resultSet); - $this->resultSet = NULL; - } - - - - /** - * Returns metadata for all columns in a result set. - * - * @return array - */ - public function getColumnsMeta() - { - $count = mysql_num_fields($this->resultSet); - $meta = array(); - for ($i = 0; $i < $count; $i++) { - // items 'name' and 'table' are required - $meta[] = (array) mysql_fetch_field($this->resultSet, $i); - } - return $meta; - } - - - - /** - * Converts database error to DibiDriverException. - * - * @throws DibiDriverException - */ - protected function throwException($sql = NULL) - { - throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql); - } - - - - /** - * Returns the connection resource. - * - * @return mixed - */ - public function getResource() - { - return $this->connection; - } - - - - /** - * Returns the result set resource. - * - * @return mixed - */ - public function getResultResource() - { - return $this->resultSet; - } - - - - /** - * Gets a information of the current database. - * - * @return DibiReflection - */ - function getDibiReflection() - {} - -} +connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['options']); // intentionally @ + } else { + $this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['options']); // intentionally @ + } + + if (!is_resource($this->connection)) { + throw new DibiDriverException(mysql_error(), mysql_errno()); + } + + if (isset($config['charset'])) { + $ok = FALSE; + if (function_exists('mysql_set_charset')) { + // affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.2.3) + $ok = @mysql_set_charset($config['charset'], $this->connection); // intentionally @ + } + if (!$ok) $ok = @mysql_query("SET NAMES '$config[charset]'", $this->connection); // intentionally @ + if (!$ok) $this->throwException(); + } + + if (isset($config['database'])) { + @mysql_select_db($config['database'], $this->connection) or $this->throwException(); // intentionally @ + } + + if (isset($config['sqlmode'])) { + if (!@mysql_query("SET sql_mode='$config[sqlmode]'", $this->connection)) $this->throwException(); // intentionally @ + } + + $this->buffered = empty($config['unbuffered']); + } + + + + /** + * Disconnects from a database. + * + * @return void + */ + public function disconnect() + { + mysql_close($this->connection); + } + + + + /** + * Executes the SQL query. + * + * @param string SQL statement. + * @return IDibiDriver|NULL + * @throws DibiDriverException + */ + public function query($sql) + { + if ($this->buffered) { + $this->resultSet = @mysql_query($sql, $this->connection); // intentionally @ + } else { + $this->resultSet = @mysql_unbuffered_query($sql, $this->connection); // intentionally @ + } + + if (mysql_errno($this->connection)) { + $this->throwException($sql); + } + + return is_resource($this->resultSet) ? clone $this : NULL; + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * + * @return int|FALSE number of rows or FALSE on error + */ + public function affectedRows() + { + return mysql_affected_rows($this->connection); + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * + * @return int|FALSE int on success or FALSE on failure + */ + public function insertId($sequence) + { + return mysql_insert_id($this->connection); + } + + + + /** + * Begins a transaction (if supported). + * @return void + * @throws DibiDriverException + */ + public function begin() + { + $this->query('START TRANSACTION'); + } + + + + /** + * Commits statements in a transaction. + * @return void + * @throws DibiDriverException + */ + public function commit() + { + $this->query('COMMIT'); + } + + + + /** + * Rollback changes in a transaction. + * @return void + * @throws DibiDriverException + */ + public function rollback() + { + $this->query('ROLLBACK'); + } + + + + /** + * Encodes data for use in an SQL statement. + * + * @param string value + * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) + * @return string encoded value + * @throws InvalidArgumentException + */ + public function escape($value, $type) + { + switch ($type) { + case dibi::FIELD_TEXT: + case dibi::FIELD_BINARY: + return "'" . mysql_real_escape_string($value, $this->connection) . "'"; + + case dibi::IDENTIFIER: + return '`' . str_replace('.', '`.`', $value) . '`'; + + case dibi::FIELD_BOOL: + return $value ? 1 : 0; + + case dibi::FIELD_DATE: + return date("'Y-m-d'", $value); + + case dibi::FIELD_DATETIME: + return date("'Y-m-d H:i:s'", $value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Decodes data from result set. + * + * @param string value + * @param string type (dibi::FIELD_BINARY) + * @return string decoded value + * @throws InvalidArgumentException + */ + public function unescape($value, $type) + { + throw new InvalidArgumentException('Unsupported type.'); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit < 0 && $offset < 1) return; + + // see http://dev.mysql.com/doc/refman/5.0/en/select.html + $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit) + . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); + } + + + + /** + * Returns the number of rows in a result set. + * + * @return int + */ + public function rowCount() + { + if (!$this->buffered) { + throw new DibiDriverException('Row count is not available for unbuffered queries.'); + } + return mysql_num_rows($this->resultSet); + } + + + + /** + * Fetches the row at current position and moves the internal cursor to the next position. + * internal usage only + * + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record + */ + public function fetch($type) + { + return mysql_fetch_array($this->resultSet, $type ? MYSQL_ASSOC : MYSQL_NUM); + } + + + + /** + * Moves cursor position without fetching row. + * + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + public function seek($row) + { + if (!$this->buffered) { + throw new DibiDriverException('Cannot seek an unbuffered result set.'); + } + + return mysql_data_seek($this->resultSet, $row); + } + + + + /** + * Frees the resources allocated for this result set. + * + * @return void + */ + public function free() + { + mysql_free_result($this->resultSet); + $this->resultSet = NULL; + } + + + + /** + * Returns metadata for all columns in a result set. + * + * @return array + */ + public function getColumnsMeta() + { + $count = mysql_num_fields($this->resultSet); + $meta = array(); + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $meta[] = (array) mysql_fetch_field($this->resultSet, $i); + } + return $meta; + } + + + + /** + * Converts database error to DibiDriverException. + * + * @throws DibiDriverException + */ + protected function throwException($sql = NULL) + { + throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql); + } + + + + /** + * Returns the connection resource. + * + * @return mixed + */ + public function getResource() + { + return $this->connection; + } + + + + /** + * Returns the result set resource. + * + * @return mixed + */ + public function getResultResource() + { + return $this->resultSet; + } + + + + /** + * Gets a information of the current database. + * + * @return DibiReflection + */ + function getDibiReflection() + {} + +} diff --git a/dibi/drivers/mysqli.php b/dibi/drivers/mysqli.php index 8991f3de..78fa0508 100644 --- a/dibi/drivers/mysqli.php +++ b/dibi/drivers/mysqli.php @@ -1,411 +1,411 @@ -connection = mysqli_init(); - @mysqli_real_connect($this->connection, $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['options']); // intentionally @ - - if ($errno = mysqli_connect_errno()) { - throw new DibiDriverException(mysqli_connect_error(), $errno); - } - - if (isset($config['charset'])) { - $ok = FALSE; - if (version_compare(PHP_VERSION , '5.1.5', '>=')) { - // affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.0.5, fixed in PHP 5.1.5) - $ok = @mysqli_set_charset($this->connection, $config['charset']); // intentionally @ - } - if (!$ok) $ok = @mysqli_query($this->connection, "SET NAMES '$config[charset]'"); // intentionally @ - if (!$ok) $this->throwException(); - } - - if (isset($config['sqlmode'])) { - if (!@mysqli_query($this->connection, "SET sql_mode='$config[sqlmode]'")) $this->throwException(); // intentionally @ - } - - $this->buffered = empty($config['unbuffered']); - } - - - - /** - * Disconnects from a database. - * - * @return void - */ - public function disconnect() - { - mysqli_close($this->connection); - } - - - - /** - * Executes the SQL query. - * - * @param string SQL statement. - * @return IDibiDriver|NULL - * @throws DibiDriverException - */ - public function query($sql) - { - $this->resultSet = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @ - - if (mysqli_errno($this->connection)) { - $this->throwException($sql); - } - - return is_object($this->resultSet) ? clone $this : NULL; - } - - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * - * @return int|FALSE number of rows or FALSE on error - */ - public function affectedRows() - { - return mysqli_affected_rows($this->connection); - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * - * @return int|FALSE int on success or FALSE on failure - */ - public function insertId($sequence) - { - return mysqli_insert_id($this->connection); - } - - - - /** - * Begins a transaction (if supported). - * @return void - * @throws DibiDriverException - */ - public function begin() - { - $this->query('START TRANSACTION'); - } - - - - /** - * Commits statements in a transaction. - * @return void - * @throws DibiDriverException - */ - public function commit() - { - $this->query('COMMIT'); - } - - - - /** - * Rollback changes in a transaction. - * @return void - * @throws DibiDriverException - */ - public function rollback() - { - $this->query('ROLLBACK'); - } - - - - /** - * Encodes data for use in an SQL statement. - * - * @param string value - * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) - * @return string encoded value - * @throws InvalidArgumentException - */ - public function escape($value, $type) - { - switch ($type) { - case dibi::FIELD_TEXT: - case dibi::FIELD_BINARY: - return "'" . mysqli_real_escape_string($this->connection, $value) . "'"; - - case dibi::IDENTIFIER: - return '`' . str_replace('.', '`.`', $value) . '`'; - - case dibi::FIELD_BOOL: - return $value ? 1 : 0; - - case dibi::FIELD_DATE: - return date("'Y-m-d'", $value); - - case dibi::FIELD_DATETIME: - return date("'Y-m-d H:i:s'", $value); - - default: - throw new InvalidArgumentException('Unsupported type.'); - } - } - - - - /** - * Decodes data from result set. - * - * @param string value - * @param string type (dibi::FIELD_BINARY) - * @return string decoded value - * @throws InvalidArgumentException - */ - public function unescape($value, $type) - { - throw new InvalidArgumentException('Unsupported type.'); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - * - * @param string &$sql The SQL query that will be modified. - * @param int $limit - * @param int $offset - * @return void - */ - public function applyLimit(&$sql, $limit, $offset) - { - if ($limit < 0 && $offset < 1) return; - - // see http://dev.mysql.com/doc/refman/5.0/en/select.html - $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit) - . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); - } - - - - /** - * Returns the number of rows in a result set. - * - * @return int - */ - public function rowCount() - { - if (!$this->buffered) { - throw new DibiDriverException('Row count is not available for unbuffered queries.'); - } - return mysqli_num_rows($this->resultSet); - } - - - - /** - * Fetches the row at current position and moves the internal cursor to the next position. - * internal usage only - * - * @param bool TRUE for associative array, FALSE for numeric - * @return array array on success, nonarray if no next record - */ - public function fetch($type) - { - return mysqli_fetch_array($this->resultSet, $type ? MYSQLI_ASSOC : MYSQLI_NUM); - } - - - - /** - * Moves cursor position without fetching row. - * - * @param int the 0-based cursor pos to seek to - * @return boolean TRUE on success, FALSE if unable to seek to specified record - * @throws DibiException - */ - public function seek($row) - { - if (!$this->buffered) { - throw new DibiDriverException('Cannot seek an unbuffered result set.'); - } - return mysqli_data_seek($this->resultSet, $row); - } - - - - /** - * Frees the resources allocated for this result set. - * - * @return void - */ - public function free() - { - mysqli_free_result($this->resultSet); - $this->resultSet = NULL; - } - - - - /** - * Returns metadata for all columns in a result set. - * - * @return array - */ - public function getColumnsMeta() - { - $count = mysqli_num_fields($this->resultSet); - $meta = array(); - for ($i = 0; $i < $count; $i++) { - // items 'name' and 'table' are required - $meta[] = (array) mysqli_fetch_field_direct($this->resultSet, $i); - } - return $meta; - } - - - - /** - * Converts database error to DibiDriverException. - * - * @throws DibiDriverException - */ - protected function throwException($sql = NULL) - { - throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql); - } - - - - /** - * Returns the connection resource. - * - * @return mysqli - */ - public function getResource() - { - return $this->connection; - } - - - - /** - * Returns the result set resource. - * - * @return mysqli_result - */ - public function getResultResource() - { - return $this->resultSet; - } - - - - /** - * Gets a information of the current database. - * - * @return DibiReflection - */ - function getDibiReflection() - {} - -} +connection = mysqli_init(); + @mysqli_real_connect($this->connection, $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['options']); // intentionally @ + + if ($errno = mysqli_connect_errno()) { + throw new DibiDriverException(mysqli_connect_error(), $errno); + } + + if (isset($config['charset'])) { + $ok = FALSE; + if (version_compare(PHP_VERSION , '5.1.5', '>=')) { + // affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.0.5, fixed in PHP 5.1.5) + $ok = @mysqli_set_charset($this->connection, $config['charset']); // intentionally @ + } + if (!$ok) $ok = @mysqli_query($this->connection, "SET NAMES '$config[charset]'"); // intentionally @ + if (!$ok) $this->throwException(); + } + + if (isset($config['sqlmode'])) { + if (!@mysqli_query($this->connection, "SET sql_mode='$config[sqlmode]'")) $this->throwException(); // intentionally @ + } + + $this->buffered = empty($config['unbuffered']); + } + + + + /** + * Disconnects from a database. + * + * @return void + */ + public function disconnect() + { + mysqli_close($this->connection); + } + + + + /** + * Executes the SQL query. + * + * @param string SQL statement. + * @return IDibiDriver|NULL + * @throws DibiDriverException + */ + public function query($sql) + { + $this->resultSet = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @ + + if (mysqli_errno($this->connection)) { + $this->throwException($sql); + } + + return is_object($this->resultSet) ? clone $this : NULL; + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * + * @return int|FALSE number of rows or FALSE on error + */ + public function affectedRows() + { + return mysqli_affected_rows($this->connection); + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * + * @return int|FALSE int on success or FALSE on failure + */ + public function insertId($sequence) + { + return mysqli_insert_id($this->connection); + } + + + + /** + * Begins a transaction (if supported). + * @return void + * @throws DibiDriverException + */ + public function begin() + { + $this->query('START TRANSACTION'); + } + + + + /** + * Commits statements in a transaction. + * @return void + * @throws DibiDriverException + */ + public function commit() + { + $this->query('COMMIT'); + } + + + + /** + * Rollback changes in a transaction. + * @return void + * @throws DibiDriverException + */ + public function rollback() + { + $this->query('ROLLBACK'); + } + + + + /** + * Encodes data for use in an SQL statement. + * + * @param string value + * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) + * @return string encoded value + * @throws InvalidArgumentException + */ + public function escape($value, $type) + { + switch ($type) { + case dibi::FIELD_TEXT: + case dibi::FIELD_BINARY: + return "'" . mysqli_real_escape_string($this->connection, $value) . "'"; + + case dibi::IDENTIFIER: + return '`' . str_replace('.', '`.`', $value) . '`'; + + case dibi::FIELD_BOOL: + return $value ? 1 : 0; + + case dibi::FIELD_DATE: + return date("'Y-m-d'", $value); + + case dibi::FIELD_DATETIME: + return date("'Y-m-d H:i:s'", $value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Decodes data from result set. + * + * @param string value + * @param string type (dibi::FIELD_BINARY) + * @return string decoded value + * @throws InvalidArgumentException + */ + public function unescape($value, $type) + { + throw new InvalidArgumentException('Unsupported type.'); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit < 0 && $offset < 1) return; + + // see http://dev.mysql.com/doc/refman/5.0/en/select.html + $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit) + . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); + } + + + + /** + * Returns the number of rows in a result set. + * + * @return int + */ + public function rowCount() + { + if (!$this->buffered) { + throw new DibiDriverException('Row count is not available for unbuffered queries.'); + } + return mysqli_num_rows($this->resultSet); + } + + + + /** + * Fetches the row at current position and moves the internal cursor to the next position. + * internal usage only + * + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record + */ + public function fetch($type) + { + return mysqli_fetch_array($this->resultSet, $type ? MYSQLI_ASSOC : MYSQLI_NUM); + } + + + + /** + * Moves cursor position without fetching row. + * + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + public function seek($row) + { + if (!$this->buffered) { + throw new DibiDriverException('Cannot seek an unbuffered result set.'); + } + return mysqli_data_seek($this->resultSet, $row); + } + + + + /** + * Frees the resources allocated for this result set. + * + * @return void + */ + public function free() + { + mysqli_free_result($this->resultSet); + $this->resultSet = NULL; + } + + + + /** + * Returns metadata for all columns in a result set. + * + * @return array + */ + public function getColumnsMeta() + { + $count = mysqli_num_fields($this->resultSet); + $meta = array(); + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $meta[] = (array) mysqli_fetch_field_direct($this->resultSet, $i); + } + return $meta; + } + + + + /** + * Converts database error to DibiDriverException. + * + * @throws DibiDriverException + */ + protected function throwException($sql = NULL) + { + throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql); + } + + + + /** + * Returns the connection resource. + * + * @return mysqli + */ + public function getResource() + { + return $this->connection; + } + + + + /** + * Returns the result set resource. + * + * @return mysqli_result + */ + public function getResultResource() + { + return $this->resultSet; + } + + + + /** + * Gets a information of the current database. + * + * @return DibiReflection + */ + function getDibiReflection() + {} + +} diff --git a/dibi/drivers/odbc.php b/dibi/drivers/odbc.php index 69cd73f0..52e70440 100644 --- a/dibi/drivers/odbc.php +++ b/dibi/drivers/odbc.php @@ -1,404 +1,404 @@ -connection = @odbc_connect($config['dsn'], $config['username'], $config['password']); // intentionally @ - } else { - $this->connection = @odbc_pconnect($config['dsn'], $config['username'], $config['password']); // intentionally @ - } - - if (!is_resource($this->connection)) { - throw new DibiDriverException(odbc_errormsg() . ' ' . odbc_error()); - } - } - - - - /** - * Disconnects from a database. - * - * @return void - */ - public function disconnect() - { - odbc_close($this->connection); - } - - - - /** - * Executes the SQL query. - * - * @param string SQL statement. - * @return IDibiDriver|NULL - * @throws DibiDriverException - */ - public function query($sql) - { - $this->resultSet = @odbc_exec($this->connection, $sql); // intentionally @ - - if ($this->resultSet === FALSE) { - $this->throwException($sql); - } - - return is_resource($this->resultSet) ? clone $this : NULL; - } - - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * - * @return int|FALSE number of rows or FALSE on error - */ - public function affectedRows() - { - return odbc_num_rows($this->resultSet); - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * - * @return int|FALSE int on success or FALSE on failure - */ - public function insertId($sequence) - { - throw new NotSupportedException('ODBC does not support autoincrementing.'); - } - - - - /** - * Begins a transaction (if supported). - * @return void - * @throws DibiDriverException - */ - public function begin() - { - if (!odbc_autocommit($this->connection, FALSE)) { - $this->throwException(); - } - } - - - - /** - * Commits statements in a transaction. - * @return void - * @throws DibiDriverException - */ - public function commit() - { - if (!odbc_commit($this->connection)) { - $this->throwException(); - } - odbc_autocommit($this->connection, TRUE); - } - - - - /** - * Rollback changes in a transaction. - * @return void - * @throws DibiDriverException - */ - public function rollback() - { - if (!odbc_rollback($this->connection)) { - $this->throwException(); - } - odbc_autocommit($this->connection, TRUE); - } - - - - /** - * Encodes data for use in an SQL statement. - * - * @param string value - * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) - * @return string encoded value - * @throws InvalidArgumentException - */ - public function escape($value, $type) - { - switch ($type) { - case dibi::FIELD_TEXT: - case dibi::FIELD_BINARY: - return "'" . str_replace("'", "''", $value) . "'"; - - case dibi::IDENTIFIER: - return '[' . str_replace('.', '].[', $value) . ']'; - - case dibi::FIELD_BOOL: - return $value ? -1 : 0; - - case dibi::FIELD_DATE: - return date("#m/d/Y#", $value); - - case dibi::FIELD_DATETIME: - return date("#m/d/Y H:i:s#", $value); - - default: - throw new InvalidArgumentException('Unsupported type.'); - } - } - - - - /** - * Decodes data from result set. - * - * @param string value - * @param string type (dibi::FIELD_BINARY) - * @return string decoded value - * @throws InvalidArgumentException - */ - public function unescape($value, $type) - { - throw new InvalidArgumentException('Unsupported type.'); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - * - * @param string &$sql The SQL query that will be modified. - * @param int $limit - * @param int $offset - * @return void - */ - public function applyLimit(&$sql, $limit, $offset) - { - // offset suppot is missing... - if ($limit >= 0) { - $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')'; - } - - if ($offset) throw new InvalidArgumentException('Offset is not implemented in driver odbc.'); - } - - - - /** - * Returns the number of rows in a result set. - * - * @return int - */ - public function rowCount() - { - // will return -1 with many drivers :-( - return odbc_num_rows($this->resultSet); - } - - - - /** - * Fetches the row at current position and moves the internal cursor to the next position. - * internal usage only - * - * @param bool TRUE for associative array, FALSE for numeric - * @return array array on success, nonarray if no next record - */ - public function fetch($type) - { - if ($type) { - return odbc_fetch_array($this->resultSet, ++$this->row); - } else { - $set = $this->resultSet; - if (!odbc_fetch_row($set, ++$this->row)) return FALSE; - $count = odbc_num_fields($set); - $cols = array(); - for ($i = 1; $i <= $count; $i++) $cols[] = odbc_result($set, $i); - return $cols; - } - } - - - - /** - * Moves cursor position without fetching row. - * - * @param int the 0-based cursor pos to seek to - * @return boolean TRUE on success, FALSE if unable to seek to specified record - * @throws DibiException - */ - public function seek($row) - { - $this->row = $row; - return TRUE; - } - - - - /** - * Frees the resources allocated for this result set. - * - * @return void - */ - public function free() - { - odbc_free_result($this->resultSet); - $this->resultSet = NULL; - } - - - - /** - * Returns metadata for all columns in a result set. - * - * @return array - */ - public function getColumnsMeta() - { - $count = odbc_num_fields($this->resultSet); - $meta = array(); - for ($i = 1; $i <= $count; $i++) { - // items 'name' and 'table' are required - $meta[] = array( - 'name' => odbc_field_name($this->resultSet, $i), - 'table' => NULL, - 'type' => odbc_field_type($this->resultSet, $i), - 'length' => odbc_field_len($this->resultSet, $i), - 'scale' => odbc_field_scale($this->resultSet, $i), - 'precision' => odbc_field_precision($this->resultSet, $i), - ); - } - return $meta; - } - - - - /** - * Converts database error to DibiDriverException. - * - * @throws DibiDriverException - */ - protected function throwException($sql = NULL) - { - throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql); - } - - - - /** - * Returns the connection resource. - * - * @return mixed - */ - public function getResource() - { - return $this->connection; - } - - - - /** - * Returns the result set resource. - * - * @return mixed - */ - public function getResultResource() - { - return $this->resultSet; - } - - - - /** - * Gets a information of the current database. - * - * @return DibiReflection - */ - function getDibiReflection() - {} - -} +connection = @odbc_connect($config['dsn'], $config['username'], $config['password']); // intentionally @ + } else { + $this->connection = @odbc_pconnect($config['dsn'], $config['username'], $config['password']); // intentionally @ + } + + if (!is_resource($this->connection)) { + throw new DibiDriverException(odbc_errormsg() . ' ' . odbc_error()); + } + } + + + + /** + * Disconnects from a database. + * + * @return void + */ + public function disconnect() + { + odbc_close($this->connection); + } + + + + /** + * Executes the SQL query. + * + * @param string SQL statement. + * @return IDibiDriver|NULL + * @throws DibiDriverException + */ + public function query($sql) + { + $this->resultSet = @odbc_exec($this->connection, $sql); // intentionally @ + + if ($this->resultSet === FALSE) { + $this->throwException($sql); + } + + return is_resource($this->resultSet) ? clone $this : NULL; + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * + * @return int|FALSE number of rows or FALSE on error + */ + public function affectedRows() + { + return odbc_num_rows($this->resultSet); + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * + * @return int|FALSE int on success or FALSE on failure + */ + public function insertId($sequence) + { + throw new NotSupportedException('ODBC does not support autoincrementing.'); + } + + + + /** + * Begins a transaction (if supported). + * @return void + * @throws DibiDriverException + */ + public function begin() + { + if (!odbc_autocommit($this->connection, FALSE)) { + $this->throwException(); + } + } + + + + /** + * Commits statements in a transaction. + * @return void + * @throws DibiDriverException + */ + public function commit() + { + if (!odbc_commit($this->connection)) { + $this->throwException(); + } + odbc_autocommit($this->connection, TRUE); + } + + + + /** + * Rollback changes in a transaction. + * @return void + * @throws DibiDriverException + */ + public function rollback() + { + if (!odbc_rollback($this->connection)) { + $this->throwException(); + } + odbc_autocommit($this->connection, TRUE); + } + + + + /** + * Encodes data for use in an SQL statement. + * + * @param string value + * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) + * @return string encoded value + * @throws InvalidArgumentException + */ + public function escape($value, $type) + { + switch ($type) { + case dibi::FIELD_TEXT: + case dibi::FIELD_BINARY: + return "'" . str_replace("'", "''", $value) . "'"; + + case dibi::IDENTIFIER: + return '[' . str_replace('.', '].[', $value) . ']'; + + case dibi::FIELD_BOOL: + return $value ? -1 : 0; + + case dibi::FIELD_DATE: + return date("#m/d/Y#", $value); + + case dibi::FIELD_DATETIME: + return date("#m/d/Y H:i:s#", $value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Decodes data from result set. + * + * @param string value + * @param string type (dibi::FIELD_BINARY) + * @return string decoded value + * @throws InvalidArgumentException + */ + public function unescape($value, $type) + { + throw new InvalidArgumentException('Unsupported type.'); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + // offset suppot is missing... + if ($limit >= 0) { + $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')'; + } + + if ($offset) throw new InvalidArgumentException('Offset is not implemented in driver odbc.'); + } + + + + /** + * Returns the number of rows in a result set. + * + * @return int + */ + public function rowCount() + { + // will return -1 with many drivers :-( + return odbc_num_rows($this->resultSet); + } + + + + /** + * Fetches the row at current position and moves the internal cursor to the next position. + * internal usage only + * + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record + */ + public function fetch($type) + { + if ($type) { + return odbc_fetch_array($this->resultSet, ++$this->row); + } else { + $set = $this->resultSet; + if (!odbc_fetch_row($set, ++$this->row)) return FALSE; + $count = odbc_num_fields($set); + $cols = array(); + for ($i = 1; $i <= $count; $i++) $cols[] = odbc_result($set, $i); + return $cols; + } + } + + + + /** + * Moves cursor position without fetching row. + * + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + public function seek($row) + { + $this->row = $row; + return TRUE; + } + + + + /** + * Frees the resources allocated for this result set. + * + * @return void + */ + public function free() + { + odbc_free_result($this->resultSet); + $this->resultSet = NULL; + } + + + + /** + * Returns metadata for all columns in a result set. + * + * @return array + */ + public function getColumnsMeta() + { + $count = odbc_num_fields($this->resultSet); + $meta = array(); + for ($i = 1; $i <= $count; $i++) { + // items 'name' and 'table' are required + $meta[] = array( + 'name' => odbc_field_name($this->resultSet, $i), + 'table' => NULL, + 'type' => odbc_field_type($this->resultSet, $i), + 'length' => odbc_field_len($this->resultSet, $i), + 'scale' => odbc_field_scale($this->resultSet, $i), + 'precision' => odbc_field_precision($this->resultSet, $i), + ); + } + return $meta; + } + + + + /** + * Converts database error to DibiDriverException. + * + * @throws DibiDriverException + */ + protected function throwException($sql = NULL) + { + throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql); + } + + + + /** + * Returns the connection resource. + * + * @return mixed + */ + public function getResource() + { + return $this->connection; + } + + + + /** + * Returns the result set resource. + * + * @return mixed + */ + public function getResultResource() + { + return $this->resultSet; + } + + + + /** + * Gets a information of the current database. + * + * @return DibiReflection + */ + function getDibiReflection() + {} + +} diff --git a/dibi/drivers/oracle.php b/dibi/drivers/oracle.php index 19957bac..993e68e6 100644 --- a/dibi/drivers/oracle.php +++ b/dibi/drivers/oracle.php @@ -1,387 +1,387 @@ -connection = @oci_new_connect($config['username'], $config['password'], $config['database'], $config['charset']); // intentionally @ - - if (!$this->connection) { - $err = oci_error(); - throw new DibiDriverException($err['message'], $err['code']); - } - } - - - - /** - * Disconnects from a database. - * - * @return void - */ - public function disconnect() - { - oci_close($this->connection); - } - - - - /** - * Executes the SQL query. - * - * @param string SQL statement. - * @return IDibiDriver|NULL - * @throws DibiDriverException - */ - public function query($sql) - { - - $this->resultSet = oci_parse($this->connection, $sql); - if ($this->resultSet) { - oci_execute($this->resultSet, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); - $err = oci_error($this->resultSet); - if ($err) { - throw new DibiDriverException($err['message'], $err['code'], $sql); - } - } else { - $this->throwException($sql); - } - - return is_resource($this->resultSet) ? clone $this : NULL; - } - - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * - * @return int|FALSE number of rows or FALSE on error - */ - public function affectedRows() - { - throw new NotImplementedException; - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * - * @return int|FALSE int on success or FALSE on failure - */ - public function insertId($sequence) - { - throw new NotSupportedException('Oracle does not support autoincrementing.'); - } - - - - /** - * Begins a transaction (if supported). - * @return void - * @throws DibiDriverException - */ - public function begin() - { - $this->autocommit = FALSE; - } - - - - /** - * Commits statements in a transaction. - * @return void - * @throws DibiDriverException - */ - public function commit() - { - if (!oci_commit($this->connection)) { - $this->throwException(); - } - $this->autocommit = TRUE; - } - - - - /** - * Rollback changes in a transaction. - * @return void - * @throws DibiDriverException - */ - public function rollback() - { - if (!oci_rollback($this->connection)) { - $this->throwException(); - } - $this->autocommit = TRUE; - } - - - - /** - * Encodes data for use in an SQL statement. - * - * @param string value - * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) - * @return string encoded value - * @throws InvalidArgumentException - */ - public function escape($value, $type) - { - switch ($type) { - case dibi::FIELD_TEXT: - case dibi::FIELD_BINARY: - return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested - - case dibi::IDENTIFIER: - return '[' . str_replace('.', '].[', $value) . ']'; // TODO: not tested - - case dibi::FIELD_BOOL: - return $value ? 1 : 0; - - case dibi::FIELD_DATE: - return date("U", $value); - - case dibi::FIELD_DATETIME: - return date("U", $value); - - default: - throw new InvalidArgumentException('Unsupported type.'); - } - } - - - - /** - * Decodes data from result set. - * - * @param string value - * @param string type (dibi::FIELD_BINARY) - * @return string decoded value - * @throws InvalidArgumentException - */ - public function unescape($value, $type) - { - throw new InvalidArgumentException('Unsupported type.'); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - * - * @param string &$sql The SQL query that will be modified. - * @param int $limit - * @param int $offset - * @return void - */ - public function applyLimit(&$sql, $limit, $offset) - { - if ($limit < 0 && $offset < 1) return; - $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); - } - - - - /** - * Returns the number of rows in a result set. - * - * @return int - */ - public function rowCount() - { - return oci_num_rows($this->resultSet); - } - - - - /** - * Fetches the row at current position and moves the internal cursor to the next position. - * internal usage only - * - * @param bool TRUE for associative array, FALSE for numeric - * @return array array on success, nonarray if no next record - */ - public function fetch($type) - { - return oci_fetch_array($this->resultSet, ($type ? OCI_ASSOC : OCI_NUM) | OCI_RETURN_NULLS); - } - - - - /** - * Moves cursor position without fetching row. - * - * @param int the 0-based cursor pos to seek to - * @return boolean TRUE on success, FALSE if unable to seek to specified record - * @throws DibiException - */ - public function seek($row) - { - throw new NotImplementedException; - } - - - - /** - * Frees the resources allocated for this result set. - * - * @return void - */ - public function free() - { - oci_free_statement($this->resultSet); - $this->resultSet = NULL; - } - - - - /** - * Returns metadata for all columns in a result set. - * - * @return array - */ - public function getColumnsMeta() - { - $count = oci_num_fields($this->resultSet); - $meta = array(); - for ($i = 1; $i <= $count; $i++) { - // items 'name' and 'table' are required - $meta[] = array( - 'name' => oci_field_name($this->resultSet, $i), - 'table' => NULL, - 'type' => oci_field_type($this->resultSet, $i), - 'size' => oci_field_size($this->resultSet, $i), - 'scale' => oci_field_scale($this->resultSet, $i), - 'precision' => oci_field_precision($this->resultSet, $i), - ); - } - return $meta; - } - - - - /** - * Converts database error to DibiDriverException. - * - * @throws DibiDriverException - */ - protected function throwException($sql = NULL) - { - $err = oci_error($this->connection); - throw new DibiDriverException($err['message'], $err['code'], $sql); - } - - - - /** - * Returns the connection resource. - * - * @return mixed - */ - public function getResource() - { - return $this->connection; - } - - - - /** - * Returns the result set resource. - * - * @return mixed - */ - public function getResultResource() - { - return $this->resultSet; - } - - - - /** - * Gets a information of the current database. - * - * @return DibiReflection - */ - function getDibiReflection() - {} - -} +connection = @oci_new_connect($config['username'], $config['password'], $config['database'], $config['charset']); // intentionally @ + + if (!$this->connection) { + $err = oci_error(); + throw new DibiDriverException($err['message'], $err['code']); + } + } + + + + /** + * Disconnects from a database. + * + * @return void + */ + public function disconnect() + { + oci_close($this->connection); + } + + + + /** + * Executes the SQL query. + * + * @param string SQL statement. + * @return IDibiDriver|NULL + * @throws DibiDriverException + */ + public function query($sql) + { + + $this->resultSet = oci_parse($this->connection, $sql); + if ($this->resultSet) { + oci_execute($this->resultSet, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); + $err = oci_error($this->resultSet); + if ($err) { + throw new DibiDriverException($err['message'], $err['code'], $sql); + } + } else { + $this->throwException($sql); + } + + return is_resource($this->resultSet) ? clone $this : NULL; + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * + * @return int|FALSE number of rows or FALSE on error + */ + public function affectedRows() + { + throw new NotImplementedException; + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * + * @return int|FALSE int on success or FALSE on failure + */ + public function insertId($sequence) + { + throw new NotSupportedException('Oracle does not support autoincrementing.'); + } + + + + /** + * Begins a transaction (if supported). + * @return void + * @throws DibiDriverException + */ + public function begin() + { + $this->autocommit = FALSE; + } + + + + /** + * Commits statements in a transaction. + * @return void + * @throws DibiDriverException + */ + public function commit() + { + if (!oci_commit($this->connection)) { + $this->throwException(); + } + $this->autocommit = TRUE; + } + + + + /** + * Rollback changes in a transaction. + * @return void + * @throws DibiDriverException + */ + public function rollback() + { + if (!oci_rollback($this->connection)) { + $this->throwException(); + } + $this->autocommit = TRUE; + } + + + + /** + * Encodes data for use in an SQL statement. + * + * @param string value + * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) + * @return string encoded value + * @throws InvalidArgumentException + */ + public function escape($value, $type) + { + switch ($type) { + case dibi::FIELD_TEXT: + case dibi::FIELD_BINARY: + return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested + + case dibi::IDENTIFIER: + return '[' . str_replace('.', '].[', $value) . ']'; // TODO: not tested + + case dibi::FIELD_BOOL: + return $value ? 1 : 0; + + case dibi::FIELD_DATE: + return date("U", $value); + + case dibi::FIELD_DATETIME: + return date("U", $value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Decodes data from result set. + * + * @param string value + * @param string type (dibi::FIELD_BINARY) + * @return string decoded value + * @throws InvalidArgumentException + */ + public function unescape($value, $type) + { + throw new InvalidArgumentException('Unsupported type.'); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit < 0 && $offset < 1) return; + $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); + } + + + + /** + * Returns the number of rows in a result set. + * + * @return int + */ + public function rowCount() + { + return oci_num_rows($this->resultSet); + } + + + + /** + * Fetches the row at current position and moves the internal cursor to the next position. + * internal usage only + * + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record + */ + public function fetch($type) + { + return oci_fetch_array($this->resultSet, ($type ? OCI_ASSOC : OCI_NUM) | OCI_RETURN_NULLS); + } + + + + /** + * Moves cursor position without fetching row. + * + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + public function seek($row) + { + throw new NotImplementedException; + } + + + + /** + * Frees the resources allocated for this result set. + * + * @return void + */ + public function free() + { + oci_free_statement($this->resultSet); + $this->resultSet = NULL; + } + + + + /** + * Returns metadata for all columns in a result set. + * + * @return array + */ + public function getColumnsMeta() + { + $count = oci_num_fields($this->resultSet); + $meta = array(); + for ($i = 1; $i <= $count; $i++) { + // items 'name' and 'table' are required + $meta[] = array( + 'name' => oci_field_name($this->resultSet, $i), + 'table' => NULL, + 'type' => oci_field_type($this->resultSet, $i), + 'size' => oci_field_size($this->resultSet, $i), + 'scale' => oci_field_scale($this->resultSet, $i), + 'precision' => oci_field_precision($this->resultSet, $i), + ); + } + return $meta; + } + + + + /** + * Converts database error to DibiDriverException. + * + * @throws DibiDriverException + */ + protected function throwException($sql = NULL) + { + $err = oci_error($this->connection); + throw new DibiDriverException($err['message'], $err['code'], $sql); + } + + + + /** + * Returns the connection resource. + * + * @return mixed + */ + public function getResource() + { + return $this->connection; + } + + + + /** + * Returns the result set resource. + * + * @return mixed + */ + public function getResultResource() + { + return $this->resultSet; + } + + + + /** + * Gets a information of the current database. + * + * @return DibiReflection + */ + function getDibiReflection() + {} + +} diff --git a/dibi/drivers/pdo.php b/dibi/drivers/pdo.php index 2b22edd3..5f50829b 100644 --- a/dibi/drivers/pdo.php +++ b/dibi/drivers/pdo.php @@ -1,427 +1,427 @@ -connection = $config['pdo']; - - } else try { - $this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']); - - } catch (PDOException $e) { - throw new DibiDriverException($e->getMessage(), $e->getCode()); - } - - if (!$this->connection) { - throw new DibiDriverException('Connecting error.'); - } - } - - - - /** - * Disconnects from a database. - * - * @return void - */ - public function disconnect() - { - $this->connection = NULL; - } - - - - /** - * Executes the SQL query. - * - * @param string SQL statement. - * @return IDibiDriver|NULL - * @throws DibiDriverException - */ - public function query($sql) - { - // must detect if SQL returns result set or num of affected rows - $cmd = strtoupper(substr(ltrim($sql), 0, 6)); - $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1); - - if (isset($list[$cmd])) { - $this->resultSet = NULL; - $this->affectedRows = $this->connection->exec($sql); - - if ($this->affectedRows === FALSE) { - $this->throwException($sql); - } - - return NULL; - - } else { - $this->resultSet = $this->connection->query($sql); - $this->affectedRows = FALSE; - - if ($this->resultSet === FALSE) { - $this->throwException($sql); - } - - return clone $this; - } - } - - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * - * @return int|FALSE number of rows or FALSE on error - */ - public function affectedRows() - { - return $this->affectedRows; - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * - * @return int|FALSE int on success or FALSE on failure - */ - public function insertId($sequence) - { - return $this->connection->lastInsertId(); - } - - - - /** - * Begins a transaction (if supported). - * @return void - * @throws DibiDriverException - */ - public function begin() - { - if (!$this->connection->beginTransaction()) { - $this->throwException(); - } - } - - - - /** - * Commits statements in a transaction. - * @return void - * @throws DibiDriverException - */ - public function commit() - { - if (!$this->connection->commit()) { - $this->throwException(); - } - } - - - - /** - * Rollback changes in a transaction. - * @return void - * @throws DibiDriverException - */ - public function rollback() - { - if (!$this->connection->rollBack()) { - $this->throwException(); - } - } - - - - /** - * Encodes data for use in an SQL statement. - * - * @param string value - * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) - * @return string encoded value - * @throws InvalidArgumentException - */ - public function escape($value, $type) - { - switch ($type) { - case dibi::FIELD_TEXT: - return $this->connection->quote($value, PDO::PARAM_STR); - - case dibi::FIELD_BINARY: - return $this->connection->quote($value, PDO::PARAM_LOB); - - case dibi::IDENTIFIER: - switch ($this->connection->getAttribute(PDO::ATTR_DRIVER_NAME)) { - case 'mysql': - return '`' . str_replace('.', '`.`', $value) . '`'; - - case 'pgsql': - $a = strrpos($value, '.'); - if ($a === FALSE) { - return '"' . str_replace('"', '""', $value) . '"'; - } else { - return substr($value, 0, $a) . '."' . str_replace('"', '""', substr($value, $a + 1)) . '"'; - } - - case 'sqlite': - case 'sqlite2': - case 'odbc': - case 'oci': // TODO: not tested - case 'mssql': - return '[' . str_replace('.', '].[', $value) . ']'; - - default: - return $value; - } - - case dibi::FIELD_BOOL: - return $this->connection->quote($value, PDO::PARAM_BOOL); - - case dibi::FIELD_DATE: - return date("'Y-m-d'", $value); - - case dibi::FIELD_DATETIME: - return date("'Y-m-d H:i:s'", $value); - - default: - throw new InvalidArgumentException('Unsupported type.'); - } - } - - - - /** - * Decodes data from result set. - * - * @param string value - * @param string type (dibi::FIELD_BINARY) - * @return string decoded value - * @throws InvalidArgumentException - */ - public function unescape($value, $type) - { - throw new InvalidArgumentException('Unsupported type.'); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - * - * @param string &$sql The SQL query that will be modified. - * @param int $limit - * @param int $offset - * @return void - */ - public function applyLimit(&$sql, $limit, $offset) - { - throw new NotSupportedException('PDO does not support applying limit or offset.'); - } - - - - /** - * Returns the number of rows in a result set. - * - * @return int - */ - public function rowCount() - { - throw new DibiDriverException('Row count is not available for unbuffered queries.'); - } - - - - /** - * Fetches the row at current position and moves the internal cursor to the next position. - * internal usage only - * - * @param bool TRUE for associative array, FALSE for numeric - * @return array array on success, nonarray if no next record - */ - public function fetch($type) - { - return $this->resultSet->fetch($type ? PDO::FETCH_ASSOC : PDO::FETCH_NUM); - } - - - - /** - * Moves cursor position without fetching row. - * - * @param int the 0-based cursor pos to seek to - * @return boolean TRUE on success, FALSE if unable to seek to specified record - * @throws DibiException - */ - public function seek($row) - { - throw new DibiDriverException('Cannot seek an unbuffered result set.'); - } - - - - /** - * Frees the resources allocated for this result set. - * - * @return void - */ - public function free() - { - $this->resultSet = NULL; - } - - - - /** - * Returns metadata for all columns in a result set. - * - * @return array - * @throws DibiException - */ - public function getColumnsMeta() - { - $count = $this->resultSet->columnCount(); - $meta = array(); - for ($i = 0; $i < $count; $i++) { - // items 'name' and 'table' are required - $info = @$this->resultSet->getColumnsMeta($i); // intentionally @ - if ($info === FALSE) { - throw new DibiDriverException('Driver does not support meta data.'); - } - $meta[] = $info; - } - return $meta; - } - - - - /** - * Converts database error to DibiDriverException. - * - * @throws DibiDriverException - */ - protected function throwException($sql = NULL) - { - $err = $this->connection->errorInfo(); - throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql); - } - - - - /** - * Returns the connection resource. - * - * @return PDO - */ - public function getResource() - { - return $this->connection; - } - - - - /** - * Returns the result set resource. - * - * @return PDOStatement - */ - public function getResultResource() - { - return $this->resultSet; - } - - - - /** - * Gets a information of the current database. - * - * @return DibiReflection - */ - function getDibiReflection() - {} - -} +connection = $config['pdo']; + + } else try { + $this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']); + + } catch (PDOException $e) { + throw new DibiDriverException($e->getMessage(), $e->getCode()); + } + + if (!$this->connection) { + throw new DibiDriverException('Connecting error.'); + } + } + + + + /** + * Disconnects from a database. + * + * @return void + */ + public function disconnect() + { + $this->connection = NULL; + } + + + + /** + * Executes the SQL query. + * + * @param string SQL statement. + * @return IDibiDriver|NULL + * @throws DibiDriverException + */ + public function query($sql) + { + // must detect if SQL returns result set or num of affected rows + $cmd = strtoupper(substr(ltrim($sql), 0, 6)); + $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1); + + if (isset($list[$cmd])) { + $this->resultSet = NULL; + $this->affectedRows = $this->connection->exec($sql); + + if ($this->affectedRows === FALSE) { + $this->throwException($sql); + } + + return NULL; + + } else { + $this->resultSet = $this->connection->query($sql); + $this->affectedRows = FALSE; + + if ($this->resultSet === FALSE) { + $this->throwException($sql); + } + + return clone $this; + } + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * + * @return int|FALSE number of rows or FALSE on error + */ + public function affectedRows() + { + return $this->affectedRows; + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * + * @return int|FALSE int on success or FALSE on failure + */ + public function insertId($sequence) + { + return $this->connection->lastInsertId(); + } + + + + /** + * Begins a transaction (if supported). + * @return void + * @throws DibiDriverException + */ + public function begin() + { + if (!$this->connection->beginTransaction()) { + $this->throwException(); + } + } + + + + /** + * Commits statements in a transaction. + * @return void + * @throws DibiDriverException + */ + public function commit() + { + if (!$this->connection->commit()) { + $this->throwException(); + } + } + + + + /** + * Rollback changes in a transaction. + * @return void + * @throws DibiDriverException + */ + public function rollback() + { + if (!$this->connection->rollBack()) { + $this->throwException(); + } + } + + + + /** + * Encodes data for use in an SQL statement. + * + * @param string value + * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) + * @return string encoded value + * @throws InvalidArgumentException + */ + public function escape($value, $type) + { + switch ($type) { + case dibi::FIELD_TEXT: + return $this->connection->quote($value, PDO::PARAM_STR); + + case dibi::FIELD_BINARY: + return $this->connection->quote($value, PDO::PARAM_LOB); + + case dibi::IDENTIFIER: + switch ($this->connection->getAttribute(PDO::ATTR_DRIVER_NAME)) { + case 'mysql': + return '`' . str_replace('.', '`.`', $value) . '`'; + + case 'pgsql': + $a = strrpos($value, '.'); + if ($a === FALSE) { + return '"' . str_replace('"', '""', $value) . '"'; + } else { + return substr($value, 0, $a) . '."' . str_replace('"', '""', substr($value, $a + 1)) . '"'; + } + + case 'sqlite': + case 'sqlite2': + case 'odbc': + case 'oci': // TODO: not tested + case 'mssql': + return '[' . str_replace('.', '].[', $value) . ']'; + + default: + return $value; + } + + case dibi::FIELD_BOOL: + return $this->connection->quote($value, PDO::PARAM_BOOL); + + case dibi::FIELD_DATE: + return date("'Y-m-d'", $value); + + case dibi::FIELD_DATETIME: + return date("'Y-m-d H:i:s'", $value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Decodes data from result set. + * + * @param string value + * @param string type (dibi::FIELD_BINARY) + * @return string decoded value + * @throws InvalidArgumentException + */ + public function unescape($value, $type) + { + throw new InvalidArgumentException('Unsupported type.'); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + throw new NotSupportedException('PDO does not support applying limit or offset.'); + } + + + + /** + * Returns the number of rows in a result set. + * + * @return int + */ + public function rowCount() + { + throw new DibiDriverException('Row count is not available for unbuffered queries.'); + } + + + + /** + * Fetches the row at current position and moves the internal cursor to the next position. + * internal usage only + * + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record + */ + public function fetch($type) + { + return $this->resultSet->fetch($type ? PDO::FETCH_ASSOC : PDO::FETCH_NUM); + } + + + + /** + * Moves cursor position without fetching row. + * + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + public function seek($row) + { + throw new DibiDriverException('Cannot seek an unbuffered result set.'); + } + + + + /** + * Frees the resources allocated for this result set. + * + * @return void + */ + public function free() + { + $this->resultSet = NULL; + } + + + + /** + * Returns metadata for all columns in a result set. + * + * @return array + * @throws DibiException + */ + public function getColumnsMeta() + { + $count = $this->resultSet->columnCount(); + $meta = array(); + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $info = @$this->resultSet->getColumnsMeta($i); // intentionally @ + if ($info === FALSE) { + throw new DibiDriverException('Driver does not support meta data.'); + } + $meta[] = $info; + } + return $meta; + } + + + + /** + * Converts database error to DibiDriverException. + * + * @throws DibiDriverException + */ + protected function throwException($sql = NULL) + { + $err = $this->connection->errorInfo(); + throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql); + } + + + + /** + * Returns the connection resource. + * + * @return PDO + */ + public function getResource() + { + return $this->connection; + } + + + + /** + * Returns the result set resource. + * + * @return PDOStatement + */ + public function getResultResource() + { + return $this->resultSet; + } + + + + /** + * Gets a information of the current database. + * + * @return DibiReflection + */ + function getDibiReflection() + {} + +} diff --git a/dibi/drivers/postgre.php b/dibi/drivers/postgre.php index 42212b37..e19c7bf4 100644 --- a/dibi/drivers/postgre.php +++ b/dibi/drivers/postgre.php @@ -1,426 +1,426 @@ -connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW); - } else { - $this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW); - } - if (DibiDriverException::catchError($msg)) { - throw new DibiDriverException($msg, 0); - } - - if (!is_resource($this->connection)) { - throw new DibiDriverException('Connecting error.'); - } - - if (isset($config['charset'])) { - DibiDriverException::tryError(); - pg_set_client_encoding($this->connection, $config['charset']); - if (DibiDriverException::catchError($msg)) { - throw new DibiDriverException($msg, 0); - } - } - - if (isset($config['schema'])) { - $this->query('SET search_path TO ' . $config['schema']); - } - - $this->escMethod = version_compare(PHP_VERSION , '5.2.0', '>='); - } - - - - /** - * Disconnects from a database. - * - * @return void - */ - public function disconnect() - { - pg_close($this->connection); - } - - - - /** - * Executes the SQL query. - * - * @param string SQL statement. - * @param bool update affected rows? - * @return IDibiDriver|NULL - * @throws DibiDriverException - */ - public function query($sql) - { - $this->resultSet = @pg_query($this->connection, $sql); // intentionally @ - - if ($this->resultSet === FALSE) { - throw new DibiDriverException(pg_last_error($this->connection), 0, $sql); - } - - return is_resource($this->resultSet) && pg_num_fields($this->resultSet) ? clone $this : NULL; - } - - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * - * @return int|FALSE number of rows or FALSE on error - */ - public function affectedRows() - { - return pg_affected_rows($this->resultSet); - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * - * @return int|FALSE int on success or FALSE on failure - */ - public function insertId($sequence) - { - if ($sequence === NULL) { - // PostgreSQL 8.1 is needed - $has = $this->query("SELECT LASTVAL()"); - } else { - $has = $this->query("SELECT CURRVAL('$sequence')"); - } - - if (!$has) return FALSE; - - $row = $this->fetch(FALSE); - $this->free(); - return is_array($row) ? $row[0] : FALSE; - } - - - - /** - * Begins a transaction (if supported). - * @return void - * @throws DibiDriverException - */ - public function begin() - { - $this->query('START TRANSACTION'); - } - - - - /** - * Commits statements in a transaction. - * @return void - * @throws DibiDriverException - */ - public function commit() - { - $this->query('COMMIT'); - } - - - - /** - * Rollback changes in a transaction. - * @return void - * @throws DibiDriverException - */ - public function rollback() - { - $this->query('ROLLBACK'); - } - - - - /** - * Encodes data for use in an SQL statement. - * - * @param string value - * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) - * @return string encoded value - * @throws InvalidArgumentException - */ - public function escape($value, $type) - { - switch ($type) { - case dibi::FIELD_TEXT: - if ($this->escMethod) { - return "'" . pg_escape_string($this->connection, $value) . "'"; - } else { - return "'" . pg_escape_string($value) . "'"; - } - - case dibi::FIELD_BINARY: - if ($this->escMethod) { - return "'" . pg_escape_bytea($this->connection, $value) . "'"; - } else { - return "'" . pg_escape_bytea($value) . "'"; - } - - case dibi::IDENTIFIER: - $a = strrpos($value, '.'); - if ($a === FALSE) { - return '"' . str_replace('"', '""', $value) . '"'; - } else { - // table.col delimite as table."col" - return substr($value, 0, $a) . '."' . str_replace('"', '""', substr($value, $a + 1)) . '"'; - } - - case dibi::FIELD_BOOL: - return $value ? 'TRUE' : 'FALSE'; - - case dibi::FIELD_DATE: - return date("'Y-m-d'", $value); - - case dibi::FIELD_DATETIME: - return date("'Y-m-d H:i:s'", $value); - - default: - throw new InvalidArgumentException('Unsupported type.'); - } - } - - - - /** - * Decodes data from result set. - * - * @param string value - * @param string type (dibi::FIELD_BINARY) - * @return string decoded value - * @throws InvalidArgumentException - */ - public function unescape($value, $type) - { - switch ($type) { - case dibi::FIELD_BINARY: - return pg_unescape_bytea($value); - - default: - throw new InvalidArgumentException('Unsupported type.'); - } - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - * - * @param string &$sql The SQL query that will be modified. - * @param int $limit - * @param int $offset - * @return void - */ - public function applyLimit(&$sql, $limit, $offset) - { - if ($limit >= 0) - $sql .= ' LIMIT ' . (int) $limit; - - if ($offset > 0) - $sql .= ' OFFSET ' . (int) $offset; - } - - - - /** - * Returns the number of rows in a result set. - * - * @return int - */ - public function rowCount() - { - return pg_num_rows($this->resultSet); - } - - - - /** - * Fetches the row at current position and moves the internal cursor to the next position. - * internal usage only - * - * @param bool TRUE for associative array, FALSE for numeric - * @return array array on success, nonarray if no next record - */ - public function fetch($type) - { - return pg_fetch_array($this->resultSet, NULL, $type ? PGSQL_ASSOC : PGSQL_NUM); - } - - - - /** - * Moves cursor position without fetching row. - * - * @param int the 0-based cursor pos to seek to - * @return boolean TRUE on success, FALSE if unable to seek to specified record - * @throws DibiException - */ - public function seek($row) - { - return pg_result_seek($this->resultSet, $row); - } - - - - /** - * Frees the resources allocated for this result set. - * - * @return void - */ - public function free() - { - pg_free_result($this->resultSet); - $this->resultSet = NULL; - } - - - - /** - * Returns metadata for all columns in a result set. - * - * @return array - */ - public function getColumnsMeta() - { - $hasTable = version_compare(PHP_VERSION , '5.2.0', '>='); - $count = pg_num_fields($this->resultSet); - $meta = array(); - for ($i = 0; $i < $count; $i++) { - // items 'name' and 'table' are required - $meta[] = array( - 'name' => pg_field_name($this->resultSet, $i), - 'table' => $hasTable ? pg_field_table($this->resultSet, $i) : NULL, - 'type' => pg_field_type($this->resultSet, $i), - 'size' => pg_field_size($this->resultSet, $i), - 'prtlen' => pg_field_prtlen($this->resultSet, $i), - ); - } - return $meta; - } - - - - /** - * Returns the connection resource. - * - * @return mixed - */ - public function getResource() - { - return $this->connection; - } - - - - /** - * Returns the result set resource. - * - * @return mixed - */ - public function getResultResource() - { - return $this->resultSet; - } - - - - /** - * Gets a information of the current database. - * - * @return DibiReflection - */ - function getDibiReflection() - {} - -} +connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW); + } else { + $this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW); + } + if (DibiDriverException::catchError($msg)) { + throw new DibiDriverException($msg, 0); + } + + if (!is_resource($this->connection)) { + throw new DibiDriverException('Connecting error.'); + } + + if (isset($config['charset'])) { + DibiDriverException::tryError(); + pg_set_client_encoding($this->connection, $config['charset']); + if (DibiDriverException::catchError($msg)) { + throw new DibiDriverException($msg, 0); + } + } + + if (isset($config['schema'])) { + $this->query('SET search_path TO ' . $config['schema']); + } + + $this->escMethod = version_compare(PHP_VERSION , '5.2.0', '>='); + } + + + + /** + * Disconnects from a database. + * + * @return void + */ + public function disconnect() + { + pg_close($this->connection); + } + + + + /** + * Executes the SQL query. + * + * @param string SQL statement. + * @param bool update affected rows? + * @return IDibiDriver|NULL + * @throws DibiDriverException + */ + public function query($sql) + { + $this->resultSet = @pg_query($this->connection, $sql); // intentionally @ + + if ($this->resultSet === FALSE) { + throw new DibiDriverException(pg_last_error($this->connection), 0, $sql); + } + + return is_resource($this->resultSet) && pg_num_fields($this->resultSet) ? clone $this : NULL; + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * + * @return int|FALSE number of rows or FALSE on error + */ + public function affectedRows() + { + return pg_affected_rows($this->resultSet); + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * + * @return int|FALSE int on success or FALSE on failure + */ + public function insertId($sequence) + { + if ($sequence === NULL) { + // PostgreSQL 8.1 is needed + $has = $this->query("SELECT LASTVAL()"); + } else { + $has = $this->query("SELECT CURRVAL('$sequence')"); + } + + if (!$has) return FALSE; + + $row = $this->fetch(FALSE); + $this->free(); + return is_array($row) ? $row[0] : FALSE; + } + + + + /** + * Begins a transaction (if supported). + * @return void + * @throws DibiDriverException + */ + public function begin() + { + $this->query('START TRANSACTION'); + } + + + + /** + * Commits statements in a transaction. + * @return void + * @throws DibiDriverException + */ + public function commit() + { + $this->query('COMMIT'); + } + + + + /** + * Rollback changes in a transaction. + * @return void + * @throws DibiDriverException + */ + public function rollback() + { + $this->query('ROLLBACK'); + } + + + + /** + * Encodes data for use in an SQL statement. + * + * @param string value + * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) + * @return string encoded value + * @throws InvalidArgumentException + */ + public function escape($value, $type) + { + switch ($type) { + case dibi::FIELD_TEXT: + if ($this->escMethod) { + return "'" . pg_escape_string($this->connection, $value) . "'"; + } else { + return "'" . pg_escape_string($value) . "'"; + } + + case dibi::FIELD_BINARY: + if ($this->escMethod) { + return "'" . pg_escape_bytea($this->connection, $value) . "'"; + } else { + return "'" . pg_escape_bytea($value) . "'"; + } + + case dibi::IDENTIFIER: + $a = strrpos($value, '.'); + if ($a === FALSE) { + return '"' . str_replace('"', '""', $value) . '"'; + } else { + // table.col delimite as table."col" + return substr($value, 0, $a) . '."' . str_replace('"', '""', substr($value, $a + 1)) . '"'; + } + + case dibi::FIELD_BOOL: + return $value ? 'TRUE' : 'FALSE'; + + case dibi::FIELD_DATE: + return date("'Y-m-d'", $value); + + case dibi::FIELD_DATETIME: + return date("'Y-m-d H:i:s'", $value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Decodes data from result set. + * + * @param string value + * @param string type (dibi::FIELD_BINARY) + * @return string decoded value + * @throws InvalidArgumentException + */ + public function unescape($value, $type) + { + switch ($type) { + case dibi::FIELD_BINARY: + return pg_unescape_bytea($value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit >= 0) + $sql .= ' LIMIT ' . (int) $limit; + + if ($offset > 0) + $sql .= ' OFFSET ' . (int) $offset; + } + + + + /** + * Returns the number of rows in a result set. + * + * @return int + */ + public function rowCount() + { + return pg_num_rows($this->resultSet); + } + + + + /** + * Fetches the row at current position and moves the internal cursor to the next position. + * internal usage only + * + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record + */ + public function fetch($type) + { + return pg_fetch_array($this->resultSet, NULL, $type ? PGSQL_ASSOC : PGSQL_NUM); + } + + + + /** + * Moves cursor position without fetching row. + * + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + public function seek($row) + { + return pg_result_seek($this->resultSet, $row); + } + + + + /** + * Frees the resources allocated for this result set. + * + * @return void + */ + public function free() + { + pg_free_result($this->resultSet); + $this->resultSet = NULL; + } + + + + /** + * Returns metadata for all columns in a result set. + * + * @return array + */ + public function getColumnsMeta() + { + $hasTable = version_compare(PHP_VERSION , '5.2.0', '>='); + $count = pg_num_fields($this->resultSet); + $meta = array(); + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $meta[] = array( + 'name' => pg_field_name($this->resultSet, $i), + 'table' => $hasTable ? pg_field_table($this->resultSet, $i) : NULL, + 'type' => pg_field_type($this->resultSet, $i), + 'size' => pg_field_size($this->resultSet, $i), + 'prtlen' => pg_field_prtlen($this->resultSet, $i), + ); + } + return $meta; + } + + + + /** + * Returns the connection resource. + * + * @return mixed + */ + public function getResource() + { + return $this->connection; + } + + + + /** + * Returns the result set resource. + * + * @return mixed + */ + public function getResultResource() + { + return $this->resultSet; + } + + + + /** + * Gets a information of the current database. + * + * @return DibiReflection + */ + function getDibiReflection() + {} + +} diff --git a/dibi/drivers/sqlite.php b/dibi/drivers/sqlite.php index f05e3bd9..de1cc3fb 100644 --- a/dibi/drivers/sqlite.php +++ b/dibi/drivers/sqlite.php @@ -1,381 +1,381 @@ -fmtDate = isset($config['format:date']) ? $config['format:date'] : 'U'; - $this->fmtDateTime = isset($config['format:datetime']) ? $config['format:datetime'] : 'U'; - - $errorMsg = ''; - if (empty($config['persistent'])) { - $this->connection = @sqlite_open($config['database'], 0666, $errorMsg); // intentionally @ - } else { - $this->connection = @sqlite_popen($config['database'], 0666, $errorMsg); // intentionally @ - } - - if (!$this->connection) { - throw new DibiDriverException($errorMsg); - } - - $this->buffered = empty($config['unbuffered']); - } - - - - /** - * Disconnects from a database. - * - * @return void - */ - public function disconnect() - { - sqlite_close($this->connection); - } - - - - /** - * Executes the SQL query. - * - * @param string SQL statement. - * @return IDibiDriver|NULL - * @throws DibiDriverException - */ - public function query($sql) - { - DibiDriverException::tryError(); - if ($this->buffered) { - $this->resultSet = sqlite_query($this->connection, $sql); - } else { - $this->resultSet = sqlite_unbuffered_query($this->connection, $sql); - } - if (DibiDriverException::catchError($msg)) { - throw new DibiDriverException($msg, sqlite_last_error($this->connection), $sql); - } - - return is_resource($this->resultSet) ? clone $this : NULL; - } - - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * - * @return int|FALSE number of rows or FALSE on error - */ - public function affectedRows() - { - return sqlite_changes($this->connection); - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * - * @return int|FALSE int on success or FALSE on failure - */ - public function insertId($sequence) - { - return sqlite_last_insert_rowid($this->connection); - } - - - - /** - * Begins a transaction (if supported). - * @return void - * @throws DibiDriverException - */ - public function begin() - { - $this->query('BEGIN'); - } - - - - /** - * Commits statements in a transaction. - * @return void - * @throws DibiDriverException - */ - public function commit() - { - $this->query('COMMIT'); - } - - - - /** - * Rollback changes in a transaction. - * @return void - * @throws DibiDriverException - */ - public function rollback() - { - $this->query('ROLLBACK'); - } - - - - /** - * Encodes data for use in an SQL statement. - * - * @param string value - * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) - * @return string encoded value - * @throws InvalidArgumentException - */ - public function escape($value, $type) - { - switch ($type) { - case dibi::FIELD_TEXT: - case dibi::FIELD_BINARY: - return "'" . sqlite_escape_string($value) . "'"; - - case dibi::IDENTIFIER: - return '[' . str_replace('.', '].[', $value) . ']'; - - case dibi::FIELD_BOOL: - return $value ? 1 : 0; - - case dibi::FIELD_DATE: - return date($this->fmtDate, $value); - - case dibi::FIELD_DATETIME: - return date($this->fmtDateTime, $value); - - default: - throw new InvalidArgumentException('Unsupported type.'); - } - } - - - - /** - * Decodes data from result set. - * - * @param string value - * @param string type (dibi::FIELD_BINARY) - * @return string decoded value - * @throws InvalidArgumentException - */ - public function unescape($value, $type) - { - throw new InvalidArgumentException('Unsupported type.'); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - * - * @param string &$sql The SQL query that will be modified. - * @param int $limit - * @param int $offset - * @return void - */ - public function applyLimit(&$sql, $limit, $offset) - { - if ($limit < 0 && $offset < 1) return; - $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); - } - - - - /** - * Returns the number of rows in a result set. - * - * @return int - */ - public function rowCount() - { - if (!$this->buffered) { - throw new DibiDriverException('Row count is not available for unbuffered queries.'); - } - return sqlite_num_rows($this->resultSet); - } - - - - /** - * Fetches the row at current position and moves the internal cursor to the next position. - * internal usage only - * - * @param bool TRUE for associative array, FALSE for numeric - * @return array array on success, nonarray if no next record - */ - public function fetch($type) - { - return sqlite_fetch_array($this->resultSet, $type ? SQLITE_ASSOC : SQLITE_NUM); - } - - - - /** - * Moves cursor position without fetching row. - * - * @param int the 0-based cursor pos to seek to - * @return boolean TRUE on success, FALSE if unable to seek to specified record - * @throws DibiException - */ - public function seek($row) - { - if (!$this->buffered) { - throw new DibiDriverException('Cannot seek an unbuffered result set.'); - } - return sqlite_seek($this->resultSet, $row); - } - - - - /** - * Frees the resources allocated for this result set. - * - * @return void - */ - public function free() - { - $this->resultSet = NULL; - } - - - - /** - * Returns metadata for all columns in a result set. - * - * @return array - */ - public function getColumnsMeta() - { - $count = sqlite_num_fields($this->resultSet); - $meta = array(); - for ($i = 0; $i < $count; $i++) { - // items 'name' and 'table' are required - $meta[] = array( - 'name' => sqlite_field_name($this->resultSet, $i), - 'table' => NULL, - ); - } - return $meta; - } - - - - /** - * Returns the connection resource. - * - * @return mixed - */ - public function getResource() - { - return $this->connection; - } - - - - /** - * Returns the result set resource. - * - * @return mixed - */ - public function getResultResource() - { - return $this->resultSet; - } - - - - /** - * Gets a information of the current database. - * - * @return DibiReflection - */ - function getDibiReflection() - {} - -} +fmtDate = isset($config['format:date']) ? $config['format:date'] : 'U'; + $this->fmtDateTime = isset($config['format:datetime']) ? $config['format:datetime'] : 'U'; + + $errorMsg = ''; + if (empty($config['persistent'])) { + $this->connection = @sqlite_open($config['database'], 0666, $errorMsg); // intentionally @ + } else { + $this->connection = @sqlite_popen($config['database'], 0666, $errorMsg); // intentionally @ + } + + if (!$this->connection) { + throw new DibiDriverException($errorMsg); + } + + $this->buffered = empty($config['unbuffered']); + } + + + + /** + * Disconnects from a database. + * + * @return void + */ + public function disconnect() + { + sqlite_close($this->connection); + } + + + + /** + * Executes the SQL query. + * + * @param string SQL statement. + * @return IDibiDriver|NULL + * @throws DibiDriverException + */ + public function query($sql) + { + DibiDriverException::tryError(); + if ($this->buffered) { + $this->resultSet = sqlite_query($this->connection, $sql); + } else { + $this->resultSet = sqlite_unbuffered_query($this->connection, $sql); + } + if (DibiDriverException::catchError($msg)) { + throw new DibiDriverException($msg, sqlite_last_error($this->connection), $sql); + } + + return is_resource($this->resultSet) ? clone $this : NULL; + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * + * @return int|FALSE number of rows or FALSE on error + */ + public function affectedRows() + { + return sqlite_changes($this->connection); + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * + * @return int|FALSE int on success or FALSE on failure + */ + public function insertId($sequence) + { + return sqlite_last_insert_rowid($this->connection); + } + + + + /** + * Begins a transaction (if supported). + * @return void + * @throws DibiDriverException + */ + public function begin() + { + $this->query('BEGIN'); + } + + + + /** + * Commits statements in a transaction. + * @return void + * @throws DibiDriverException + */ + public function commit() + { + $this->query('COMMIT'); + } + + + + /** + * Rollback changes in a transaction. + * @return void + * @throws DibiDriverException + */ + public function rollback() + { + $this->query('ROLLBACK'); + } + + + + /** + * Encodes data for use in an SQL statement. + * + * @param string value + * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) + * @return string encoded value + * @throws InvalidArgumentException + */ + public function escape($value, $type) + { + switch ($type) { + case dibi::FIELD_TEXT: + case dibi::FIELD_BINARY: + return "'" . sqlite_escape_string($value) . "'"; + + case dibi::IDENTIFIER: + return '[' . str_replace('.', '].[', $value) . ']'; + + case dibi::FIELD_BOOL: + return $value ? 1 : 0; + + case dibi::FIELD_DATE: + return date($this->fmtDate, $value); + + case dibi::FIELD_DATETIME: + return date($this->fmtDateTime, $value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Decodes data from result set. + * + * @param string value + * @param string type (dibi::FIELD_BINARY) + * @return string decoded value + * @throws InvalidArgumentException + */ + public function unescape($value, $type) + { + throw new InvalidArgumentException('Unsupported type.'); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit < 0 && $offset < 1) return; + $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); + } + + + + /** + * Returns the number of rows in a result set. + * + * @return int + */ + public function rowCount() + { + if (!$this->buffered) { + throw new DibiDriverException('Row count is not available for unbuffered queries.'); + } + return sqlite_num_rows($this->resultSet); + } + + + + /** + * Fetches the row at current position and moves the internal cursor to the next position. + * internal usage only + * + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record + */ + public function fetch($type) + { + return sqlite_fetch_array($this->resultSet, $type ? SQLITE_ASSOC : SQLITE_NUM); + } + + + + /** + * Moves cursor position without fetching row. + * + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + public function seek($row) + { + if (!$this->buffered) { + throw new DibiDriverException('Cannot seek an unbuffered result set.'); + } + return sqlite_seek($this->resultSet, $row); + } + + + + /** + * Frees the resources allocated for this result set. + * + * @return void + */ + public function free() + { + $this->resultSet = NULL; + } + + + + /** + * Returns metadata for all columns in a result set. + * + * @return array + */ + public function getColumnsMeta() + { + $count = sqlite_num_fields($this->resultSet); + $meta = array(); + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $meta[] = array( + 'name' => sqlite_field_name($this->resultSet, $i), + 'table' => NULL, + ); + } + return $meta; + } + + + + /** + * Returns the connection resource. + * + * @return mixed + */ + public function getResource() + { + return $this->connection; + } + + + + /** + * Returns the result set resource. + * + * @return mixed + */ + public function getResultResource() + { + return $this->resultSet; + } + + + + /** + * Gets a information of the current database. + * + * @return DibiReflection + */ + function getDibiReflection() + {} + +} diff --git a/dibi/libs/DibiConnection.php b/dibi/libs/DibiConnection.php index 1368f5c0..4784189f 100644 --- a/dibi/libs/DibiConnection.php +++ b/dibi/libs/DibiConnection.php @@ -1,497 +1,501 @@ -config = $config; - $this->driver = new $class; - - if (empty($config['lazy'])) { - $this->connect(); - } - } - - - - /** - * Automatically frees the resources allocated for this result set. - * - * @return void - */ - public function __destruct() - { - // disconnects and rolls back transaction - do not rely on auto-disconnect and rollback! - $this->disconnect(); - } - - - - /** - * Connects to a database. - * - * @return void - */ - final protected function connect() - { - if (!$this->connected) { - $this->driver->connect($this->config); - $this->connected = TRUE; - dibi::notify($this, 'connected'); - } - } - - - - /** - * Disconnects from a database. - * - * @return void - */ - final public function disconnect() - { - if ($this->connected) { - if ($this->inTxn) { - $this->rollback(); - } - $this->driver->disconnect(); - $this->connected = FALSE; - dibi::notify($this, 'disconnected'); - } - } - - - - /** - * Returns TRUE when connection was established. - * - * @return bool - */ - final public function isConnected() - { - return $this->connected; - } - - - - /** - * Returns configuration variable. If no $key is passed, returns the entire array. - * - * @see self::__construct - * @param string - * @param mixed default value to use if key not found - * @return mixed - */ - final public function getConfig($key = NULL, $default = NULL) - { - if ($key === NULL) { - return $this->config; - - } elseif (isset($this->config[$key])) { - return $this->config[$key]; - - } else { - return $default; - } - } - - - - /** - * Apply configuration alias or default values. - * - * @param array connect configuration - * @param string key - * @param string alias key - * @return void - */ - public static function alias(&$config, $key, $alias=NULL) - { - if (isset($config[$key])) return; - - if ($alias !== NULL && isset($config[$alias])) { - $config[$key] = $config[$alias]; - unset($config[$alias]); - } else { - $config[$key] = NULL; - } - } - - - - /** - * Returns the connection resource. - * - * @return resource - */ - final public function getResource() - { - return $this->driver->getResource(); - } - - - - /** - * Generates (translates) and executes SQL query. - * - * @param array|mixed one or more arguments - * @return DibiResult|NULL result set object (if any) - * @throws DibiException - */ - final public function query($args) - { - $args = func_get_args(); - $this->connect(); - $trans = new DibiTranslator($this->driver); - if ($trans->translate($args)) { - return $this->nativeQuery($trans->sql); - } else { - throw new DibiException('SQL translate error: ' . $trans->sql); - } - } - - - - /** - * Generates and prints SQL query. - * - * @param array|mixed one or more arguments - * @return bool - */ - final public function test($args) - { - $args = func_get_args(); - $this->connect(); - $trans = new DibiTranslator($this->driver); - $ok = $trans->translate($args); - dibi::dump($trans->sql); - return $ok; - } - - - - /** - * Executes the SQL query. - * - * @param string SQL statement. - * @return DibiResult|NULL result set object (if any) - * @throws DibiException - */ - final public function nativeQuery($sql) - { - $this->connect(); - - dibi::$numOfQueries++; - dibi::$sql = $sql; - dibi::$elapsedTime = FALSE; - $time = -microtime(TRUE); - dibi::notify($this, 'beforeQuery', $sql); - - if ($res = $this->driver->query($sql)) { // intentionally = - $res = new DibiResult($res, $this->config); - } - - $time += microtime(TRUE); - dibi::$elapsedTime = $time; - dibi::$totalTime += $time; - dibi::notify($this, 'afterQuery', $res); - - return $res; - } - - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * - * @return int number of rows - * @throws DibiException - */ - public function affectedRows() - { - $rows = $this->driver->affectedRows(); - if (!is_int($rows) || $rows < 0) throw new DibiException('Cannot retrieve number of affected rows.'); - return $rows; - } - - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * - * @param string optional sequence name - * @return int - * @throws DibiException - */ - public function insertId($sequence = NULL) - { - $id = $this->driver->insertId($sequence); - if ($id < 1) throw new DibiException('Cannot retrieve last generated ID.'); - return (int) $id; - } - - - - /** - * Begins a transaction (if supported). - * @return void - */ - public function begin() - { - $this->connect(); - if ($this->inTxn) { - throw new DibiException('There is already an active transaction.'); - } - $this->driver->begin(); - $this->inTxn = TRUE; - dibi::notify($this, 'begin'); - } - - - - /** - * Commits statements in a transaction. - * @return void - */ - public function commit() - { - if (!$this->inTxn) { - throw new DibiException('There is no active transaction.'); - } - $this->driver->commit(); - $this->inTxn = FALSE; - dibi::notify($this, 'commit'); - } - - - - /** - * Rollback changes in a transaction. - * @return void - */ - public function rollback() - { - if (!$this->inTxn) { - throw new DibiException('There is no active transaction.'); - } - $this->driver->rollback(); - $this->inTxn = FALSE; - dibi::notify($this, 'rollback'); - } - - - - /** - * Encodes data for use in an SQL statement. - * - * @param string unescaped string - * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) - * @return string escaped and quoted string - */ - public function escape($value, $type = dibi::FIELD_TEXT) - { - $this->connect(); // MySQL & PDO require connection - return $this->driver->escape($value, $type); - } - - - - /** - * Decodes data from result set. - * - * @param string value - * @param string type (dibi::FIELD_BINARY) - * @return string decoded value - */ - public function unescape($value, $type = dibi::FIELD_BINARY) - { - return $this->driver->unescape($value, $type); - } - - - - /** - * Delimites identifier (table's or column's name, etc.). - * - * @param string identifier - * @return string delimited identifier - */ - public function delimite($value) - { - return $this->driver->escape($value, dibi::IDENTIFIER); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - * - * @param string &$sql The SQL query that will be modified. - * @param int $limit - * @param int $offset - * @return void - */ - public function applyLimit(&$sql, $limit, $offset) - { - $this->driver->applyLimit($sql, $limit, $offset); - } - - - - /** - * Import SQL dump from file - extreme fast! - * - * @param string filename - * @return int count of sql commands - */ - public function loadFile($file) - { - $this->connect(); - - @set_time_limit(0); // intentionally @ - - $handle = @fopen($file, 'r'); // intentionally @ - if (!$handle) { - throw new FileNotFoundException("Cannot open file '$file'."); - } - - $count = 0; - $sql = ''; - while (!feof($handle)) { - $s = fgets($handle); - $sql .= $s; - if (substr(rtrim($s), -1) === ';') { - $this->driver->query($sql); - $sql = ''; - $count++; - } - } - fclose($handle); - return $count; - } - - - - /** - * Gets a information of the current database. - * - * @return DibiReflection - */ - public function getDibiReflection() - { - throw new NotImplementedException; - } - - - - /** - * Prevents unserialization. - */ - public function __wakeup() - { - throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.'); - } - - - - /** - * Prevents serialization. - */ - public function __sleep() - { - throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.'); - } - -} +config = $config; + $this->driver = new $class; + + if (empty($config['lazy'])) { + $this->connect(); + } + } + + + + /** + * Automatically frees the resources allocated for this result set. + * + * @return void + */ + public function __destruct() + { + // disconnects and rolls back transaction - do not rely on auto-disconnect and rollback! + $this->disconnect(); + } + + + + /** + * Connects to a database. + * + * @return void + */ + final protected function connect() + { + if (!$this->connected) { + $this->driver->connect($this->config); + $this->connected = TRUE; + dibi::notify($this, 'connected'); + } + } + + + + /** + * Disconnects from a database. + * + * @return void + */ + final public function disconnect() + { + if ($this->connected) { + if ($this->inTxn) { + $this->rollback(); + } + $this->driver->disconnect(); + $this->connected = FALSE; + dibi::notify($this, 'disconnected'); + } + } + + + + /** + * Returns TRUE when connection was established. + * + * @return bool + */ + final public function isConnected() + { + return $this->connected; + } + + + + /** + * Returns configuration variable. If no $key is passed, returns the entire array. + * + * @see self::__construct + * @param string + * @param mixed default value to use if key not found + * @return mixed + */ + final public function getConfig($key = NULL, $default = NULL) + { + if ($key === NULL) { + return $this->config; + + } elseif (isset($this->config[$key])) { + return $this->config[$key]; + + } else { + return $default; + } + } + + + + /** + * Apply configuration alias or default values. + * + * @param array connect configuration + * @param string key + * @param string alias key + * @return void + */ + public static function alias(&$config, $key, $alias=NULL) + { + if (isset($config[$key])) return; + + if ($alias !== NULL && isset($config[$alias])) { + $config[$key] = $config[$alias]; + unset($config[$alias]); + } else { + $config[$key] = NULL; + } + } + + + + /** + * Returns the connection resource. + * + * @return resource + */ + final public function getResource() + { + return $this->driver->getResource(); + } + + + + /** + * Generates (translates) and executes SQL query. + * + * @param array|mixed one or more arguments + * @return DibiResult|NULL result set object (if any) + * @throws DibiException + */ + final public function query($args) + { + $args = func_get_args(); + $this->connect(); + $trans = new DibiTranslator($this->driver); + if ($trans->translate($args)) { + return $this->nativeQuery($trans->sql); + } else { + throw new DibiException('SQL translate error: ' . $trans->sql); + } + } + + + + /** + * Generates and prints SQL query. + * + * @param array|mixed one or more arguments + * @return bool + */ + final public function test($args) + { + $args = func_get_args(); + $this->connect(); + $trans = new DibiTranslator($this->driver); + $ok = $trans->translate($args); + dibi::dump($trans->sql); + return $ok; + } + + + + /** + * Executes the SQL query. + * + * @param string SQL statement. + * @return DibiResult|NULL result set object (if any) + * @throws DibiException + */ + final public function nativeQuery($sql) + { + $this->connect(); + + dibi::$numOfQueries++; + dibi::$sql = $sql; + dibi::$elapsedTime = FALSE; + $time = -microtime(TRUE); + dibi::notify($this, 'beforeQuery', $sql); + + if ($res = $this->driver->query($sql)) { // intentionally = + $res = new DibiResult($res, $this->config); + } + + $time += microtime(TRUE); + dibi::$elapsedTime = $time; + dibi::$totalTime += $time; + dibi::notify($this, 'afterQuery', $res); + + return $res; + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * + * @return int number of rows + * @throws DibiException + */ + public function affectedRows() + { + $rows = $this->driver->affectedRows(); + if (!is_int($rows) || $rows < 0) throw new DibiException('Cannot retrieve number of affected rows.'); + return $rows; + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * + * @param string optional sequence name + * @return int + * @throws DibiException + */ + public function insertId($sequence = NULL) + { + $id = $this->driver->insertId($sequence); + if ($id < 1) throw new DibiException('Cannot retrieve last generated ID.'); + return (int) $id; + } + + + + /** + * Begins a transaction (if supported). + * @return void + */ + public function begin() + { + $this->connect(); + if ($this->inTxn) { + throw new DibiException('There is already an active transaction.'); + } + $this->driver->begin(); + $this->inTxn = TRUE; + dibi::notify($this, 'begin'); + } + + + + /** + * Commits statements in a transaction. + * @return void + */ + public function commit() + { + if (!$this->inTxn) { + throw new DibiException('There is no active transaction.'); + } + $this->driver->commit(); + $this->inTxn = FALSE; + dibi::notify($this, 'commit'); + } + + + + /** + * Rollback changes in a transaction. + * @return void + */ + public function rollback() + { + if (!$this->inTxn) { + throw new DibiException('There is no active transaction.'); + } + $this->driver->rollback(); + $this->inTxn = FALSE; + dibi::notify($this, 'rollback'); + } + + + + /** + * Encodes data for use in an SQL statement. + * + * @param string unescaped string + * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...) + * @return string escaped and quoted string + */ + public function escape($value, $type = dibi::FIELD_TEXT) + { + $this->connect(); // MySQL & PDO require connection + return $this->driver->escape($value, $type); + } + + + + /** + * Decodes data from result set. + * + * @param string value + * @param string type (dibi::FIELD_BINARY) + * @return string decoded value + */ + public function unescape($value, $type = dibi::FIELD_BINARY) + { + return $this->driver->unescape($value, $type); + } + + + + /** + * Delimites identifier (table's or column's name, etc.). + * + * @param string identifier + * @return string delimited identifier + */ + public function delimite($value) + { + return $this->driver->escape($value, dibi::IDENTIFIER); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + $this->driver->applyLimit($sql, $limit, $offset); + } + + + + /** + * Import SQL dump from file - extreme fast! + * + * @param string filename + * @return int count of sql commands + */ + public function loadFile($file) + { + $this->connect(); + + @set_time_limit(0); // intentionally @ + + $handle = @fopen($file, 'r'); // intentionally @ + if (!$handle) { + throw new FileNotFoundException("Cannot open file '$file'."); + } + + $count = 0; + $sql = ''; + while (!feof($handle)) { + $s = fgets($handle); + $sql .= $s; + if (substr(rtrim($s), -1) === ';') { + $this->driver->query($sql); + $sql = ''; + $count++; + } + } + fclose($handle); + return $count; + } + + + + /** + * Gets a information of the current database. + * + * @return DibiReflection + */ + public function getDibiReflection() + { + throw new NotImplementedException; + } + + + + /** + * Prevents unserialization. + */ + public function __wakeup() + { + throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.'); + } + + + + /** + * Prevents serialization. + */ + public function __sleep() + { + throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.'); + } + +} diff --git a/dibi/libs/DibiDataSource.php b/dibi/libs/DibiDataSource.php index 70f385a5..afd68bbf 100644 --- a/dibi/libs/DibiDataSource.php +++ b/dibi/libs/DibiDataSource.php @@ -1,92 +1,92 @@ -sql = $sql; - } else { - // SQL command - $this->sql = '(' . $sql . ') AS [source]'; - } - - $this->connection = $connection === NULL ? dibi::getConnection() : $connection; - } - - - - /** - * @param int offset - * @param int limit - * @param array columns - * @return ArrayIterator - */ - public function getIterator($offset = NULL, $limit = NULL, $cols = NULL) - { - return $this->connection->query(' - SELECT * - FROM', $this->sql, ' - %ofs %lmt', $offset, $limit - ); - } - - - - /** - * @return int - */ - public function count() - { - if ($this->count === NULL) { - $this->count = $this->connection->query(' - SELECT COUNT(*) FROM', $this->sql - )->fetchSingle(); - } - return $this->count; - } - -} +sql = $sql; + } else { + // SQL command + $this->sql = '(' . $sql . ') AS [source]'; + } + + $this->connection = $connection === NULL ? dibi::getConnection() : $connection; + } + + + + /** + * @param int offset + * @param int limit + * @param array columns + * @return ArrayIterator + */ + public function getIterator($offset = NULL, $limit = NULL, $cols = NULL) + { + return $this->connection->query(' + SELECT * + FROM', $this->sql, ' + %ofs %lmt', $offset, $limit + ); + } + + + + /** + * @return int + */ + public function count() + { + if ($this->count === NULL) { + $this->count = $this->connection->query(' + SELECT COUNT(*) FROM', $this->sql + )->fetchSingle(); + } + return $this->count; + } + +} diff --git a/dibi/libs/DibiException.php b/dibi/libs/DibiException.php index 18b077c8..6ef95fda 100644 --- a/dibi/libs/DibiException.php +++ b/dibi/libs/DibiException.php @@ -1,159 +1,158 @@ -sql = $sql; - dibi::notify(NULL, 'exception', $this); - } - - - - /** - * @return string The SQL passed to the constructor - */ - final public function getSql() - { - return $this->sql; - } - - - - /** - * @return string string represenation of exception with SQL command - */ - public function __toString() - { - return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : ''); - } - - - - /********************* interface Nette::IDebuggable ****************d*g**/ - - - /** - * Returns custom panels. - * @return array - */ - public function getPanels() - { - $panels = array(); - if ($this->sql !== NULL) { - $panels['SQL'] = array( - 'expanded' => TRUE, - 'content' => dibi::dump($this->sql, TRUE), - ); - } - return $panels; - } - - - - /********************* error catching ****************d*g**/ - - - - /** - * Starts catching potential errors/warnings - * - * @return void - */ - public static function tryError() - { - set_error_handler(array(__CLASS__, '_errorHandler'), E_ALL); - self::$errorMsg = NULL; - } - - - - /** - * Returns catched error/warning message. - * - * @param string catched message - * @return bool - */ - public static function catchError(& $message) - { - restore_error_handler(); - $message = self::$errorMsg; - self::$errorMsg = NULL; - return $message !== NULL; - } - - - - /** - * Internal error handler. Do not call directly. - */ - public static function _errorHandler($code, $message) - { - restore_error_handler(); - - if (ini_get('html_errors')) { - $message = strip_tags($message); - $message = html_entity_decode($message); - } - - self::$errorMsg = $message; - } - +sql = $sql; + dibi::notify(NULL, 'exception', $this); + } + + + + /** + * @return string The SQL passed to the constructor + */ + final public function getSql() + { + return $this->sql; + } + + + + /** + * @return string string represenation of exception with SQL command + */ + public function __toString() + { + return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : ''); + } + + + + /********************* interface Nette::IDebuggable ****************d*g**/ + + + /** + * Returns custom panels. + * @return array + */ + public function getPanels() + { + $panels = array(); + if ($this->sql !== NULL) { + $panels['SQL'] = array( + 'expanded' => TRUE, + 'content' => dibi::dump($this->sql, TRUE), + ); + } + return $panels; + } + + + + /********************* error catching ****************d*g**/ + + + + /** + * Starts catching potential errors/warnings + * + * @return void + */ + public static function tryError() + { + set_error_handler(array(__CLASS__, '_errorHandler'), E_ALL); + self::$errorMsg = NULL; + } + + + + /** + * Returns catched error/warning message. + * + * @param string catched message + * @return bool + */ + public static function catchError(& $message) + { + restore_error_handler(); + $message = self::$errorMsg; + self::$errorMsg = NULL; + return $message !== NULL; + } + + + + /** + * Internal error handler. Do not call directly. + */ + public static function _errorHandler($code, $message) + { + restore_error_handler(); + + if (ini_get('html_errors')) { + $message = strip_tags($message); + $message = html_entity_decode($message); + } + + self::$errorMsg = $message; + } + } \ No newline at end of file diff --git a/dibi/libs/DibiFluent.php b/dibi/libs/DibiFluent.php index d18a4e2d..43beabc6 100644 --- a/dibi/libs/DibiFluent.php +++ b/dibi/libs/DibiFluent.php @@ -1,329 +1,329 @@ - array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY', - 'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET', '%end'), - 'UPDATE' => array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT', '%end'), - 'INSERT' => array('INSERT', 'INTO', 'VALUES', 'SELECT', '%end'), - 'DELETE' => array('DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT', '%end'), - ); - - /** @var array */ - public static $separators = array( - 'SELECT' => ',', - 'FROM' => FALSE, - 'WHERE' => 'AND', - 'GROUP BY' => ',', - 'HAVING' => 'AND', - 'ORDER BY' => ',', - 'LIMIT' => FALSE, - 'OFFSET' => FALSE, - 'SET' => ',', - 'VALUES' => ',', - 'INTO' => FALSE, - ); - - /** @var DibiConnection */ - private $connection; - - /** @var string */ - private $command; - - /** @var array */ - private $clauses = array(); - - /** @var array */ - private $flags = array(); - - /** @var array */ - private $cursor; - - - - /** - * @param DibiConnection - */ - public function __construct(DibiConnection $connection) - { - $this->connection = $connection; - } - - - - /** - * Appends new argument to the clause. - * @param string clause name - * @param array arguments - * @return DibiFluent provides a fluent interface - */ - public function __call($clause, $args) - { - $clause = self::_clause($clause); - - // lazy initialization - if ($this->command === NULL) { - if (isset(self::$masks[$clause])) { - $this->clauses = array_fill_keys(self::$masks[$clause], NULL); - } - $this->cursor = & $this->clauses[$clause]; - $this->cursor = array(); - $this->command = $clause; - } - - // special types or argument - if (count($args) === 1) { - $arg = $args[0]; - // TODO: really ignore TRUE? - if ($arg === TRUE) { - $args = array(); - - } elseif (is_string($arg) && preg_match('#^[a-z][a-z0-9_.]*$#i', $arg)) { - $args = array('%n', $arg); - } - } - - if (array_key_exists($clause, $this->clauses)) { - // append to clause - $this->cursor = & $this->clauses[$clause]; - - // TODO: really delete? - if ($args === array(FALSE)) { - $this->cursor = NULL; - return $this; - } - - if (isset(self::$separators[$clause])) { - $sep = self::$separators[$clause]; - if ($sep === FALSE) { - $this->cursor = array(); - - } elseif (!empty($this->cursor)) { - $this->cursor[] = $sep; - } - } - - } else { - // append to currect flow - if ($args === array(FALSE)) { - return $this; - } - - $this->cursor[] = $clause; - } - - if ($this->cursor === NULL) { - $this->cursor = array(); - } - - array_splice($this->cursor, count($this->cursor), 0, $args); - return $this; - } - - - - /** - * Switch to a clause. - * @param string clause name - * @return DibiFluent provides a fluent interface - */ - public function clause($clause, $remove = FALSE) - { - $this->cursor = & $this->clauses[self::_clause($clause)]; - - if ($remove) { - $this->cursor = NULL; - - } elseif ($this->cursor === NULL) { - $this->cursor = array(); - } - - return $this; - } - - - - /** - * Change a SQL flag. - * @param string flag name - * @param bool value - * @return DibiFluent provides a fluent interface - */ - public function setFlag($flag, $value = TRUE) - { - $flag = strtoupper($flag); - if ($value) { - $this->flags[$flag] = TRUE; - } else { - unset($this->flags[$flag]); - } - return $this; - } - - - - /** - * Is a flag set? - * @param string flag name - * @return bool - */ - final public function getFlag($flag, $value = TRUE) - { - return isset($this->flags[strtoupper($flag)]); - } - - - - /** - * Returns SQL command. - * @return string - */ - final public function getCommand() - { - return $this->command; - } - - - - /** - * Generates and executes SQL query. - * @return DibiResult|NULL result set object (if any) - * @throws DibiException - */ - public function execute() - { - return $this->connection->query($this->_export()); - } - - - - /** - * Generates, executes SQL query and fetches the single row. - * @return array|FALSE array on success, FALSE if no next record - * @throws DibiException - */ - public function fetch() - { - if ($this->command === 'SELECT') { - $this->clauses['LIMIT'] = array(1); - } - return $this->connection->query($this->_export())->fetch(); - } - - - - /** - * Generates and prints SQL query or it's part. - * @param string clause name - * @return bool - */ - public function test($clause = NULL) - { - return $this->connection->test($this->_export($clause)); - } - - - - /** - * Generates parameters for DibiTranslator. - * @param string clause name - * @return array - */ - protected function _export($clause = NULL) - { - if ($clause === NULL) { - $data = $this->clauses; - - } else { - $clause = self::_clause($clause); - if (array_key_exists($clause, $this->clauses)) { - $data = array($clause => $this->clauses[$clause]); - } else { - return array(); - } - } - - $args = array(); - foreach ($data as $clause => $statement) { - if ($statement !== NULL) { - if ($clause[0] !== '%') { - $args[] = $clause; - if ($clause === $this->command) { - $args[] = implode(' ', array_keys($this->flags)); - } - } - array_splice($args, count($args), 0, $statement); - } - } - return $args; - } - - - - /** - * Format camelCase clause name to UPPER CASE. - * @param string - * @return string - */ - private static function _clause($s) - { - if ($s === 'order' || $s === 'group') { - $s .= 'By'; - trigger_error("Did you mean '$s'?", E_USER_NOTICE); - } - return strtoupper(preg_replace('#[A-Z]#', ' $0', $s)); - - } - - - - /** - * Returns (highlighted) SQL query. - * @return string - */ - final public function __toString() - { - ob_start(); - $this->test(); - return ob_get_clean(); - } - -} - - -// PHP < 5.2 compatibility -if (!function_exists('array_fill_keys')) { - function array_fill_keys($keys, $value) - { - return array_combine($keys, array_fill(0, count($keys), $value)); - } -} + array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY', + 'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET', '%end'), + 'UPDATE' => array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT', '%end'), + 'INSERT' => array('INSERT', 'INTO', 'VALUES', 'SELECT', '%end'), + 'DELETE' => array('DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT', '%end'), + ); + + /** @var array */ + public static $separators = array( + 'SELECT' => ',', + 'FROM' => FALSE, + 'WHERE' => 'AND', + 'GROUP BY' => ',', + 'HAVING' => 'AND', + 'ORDER BY' => ',', + 'LIMIT' => FALSE, + 'OFFSET' => FALSE, + 'SET' => ',', + 'VALUES' => ',', + 'INTO' => FALSE, + ); + + /** @var DibiConnection */ + private $connection; + + /** @var string */ + private $command; + + /** @var array */ + private $clauses = array(); + + /** @var array */ + private $flags = array(); + + /** @var array */ + private $cursor; + + + + /** + * @param DibiConnection + */ + public function __construct(DibiConnection $connection) + { + $this->connection = $connection; + } + + + + /** + * Appends new argument to the clause. + * @param string clause name + * @param array arguments + * @return DibiFluent provides a fluent interface + */ + public function __call($clause, $args) + { + $clause = self::_clause($clause); + + // lazy initialization + if ($this->command === NULL) { + if (isset(self::$masks[$clause])) { + $this->clauses = array_fill_keys(self::$masks[$clause], NULL); + } + $this->cursor = & $this->clauses[$clause]; + $this->cursor = array(); + $this->command = $clause; + } + + // special types or argument + if (count($args) === 1) { + $arg = $args[0]; + // TODO: really ignore TRUE? + if ($arg === TRUE) { + $args = array(); + + } elseif (is_string($arg) && preg_match('#^[a-z][a-z0-9_.]*$#i', $arg)) { + $args = array('%n', $arg); + } + } + + if (array_key_exists($clause, $this->clauses)) { + // append to clause + $this->cursor = & $this->clauses[$clause]; + + // TODO: really delete? + if ($args === array(FALSE)) { + $this->cursor = NULL; + return $this; + } + + if (isset(self::$separators[$clause])) { + $sep = self::$separators[$clause]; + if ($sep === FALSE) { + $this->cursor = array(); + + } elseif (!empty($this->cursor)) { + $this->cursor[] = $sep; + } + } + + } else { + // append to currect flow + if ($args === array(FALSE)) { + return $this; + } + + $this->cursor[] = $clause; + } + + if ($this->cursor === NULL) { + $this->cursor = array(); + } + + array_splice($this->cursor, count($this->cursor), 0, $args); + return $this; + } + + + + /** + * Switch to a clause. + * @param string clause name + * @return DibiFluent provides a fluent interface + */ + public function clause($clause, $remove = FALSE) + { + $this->cursor = & $this->clauses[self::_clause($clause)]; + + if ($remove) { + $this->cursor = NULL; + + } elseif ($this->cursor === NULL) { + $this->cursor = array(); + } + + return $this; + } + + + + /** + * Change a SQL flag. + * @param string flag name + * @param bool value + * @return DibiFluent provides a fluent interface + */ + public function setFlag($flag, $value = TRUE) + { + $flag = strtoupper($flag); + if ($value) { + $this->flags[$flag] = TRUE; + } else { + unset($this->flags[$flag]); + } + return $this; + } + + + + /** + * Is a flag set? + * @param string flag name + * @return bool + */ + final public function getFlag($flag, $value = TRUE) + { + return isset($this->flags[strtoupper($flag)]); + } + + + + /** + * Returns SQL command. + * @return string + */ + final public function getCommand() + { + return $this->command; + } + + + + /** + * Generates and executes SQL query. + * @return DibiResult|NULL result set object (if any) + * @throws DibiException + */ + public function execute() + { + return $this->connection->query($this->_export()); + } + + + + /** + * Generates, executes SQL query and fetches the single row. + * @return array|FALSE array on success, FALSE if no next record + * @throws DibiException + */ + public function fetch() + { + if ($this->command === 'SELECT') { + $this->clauses['LIMIT'] = array(1); + } + return $this->connection->query($this->_export())->fetch(); + } + + + + /** + * Generates and prints SQL query or it's part. + * @param string clause name + * @return bool + */ + public function test($clause = NULL) + { + return $this->connection->test($this->_export($clause)); + } + + + + /** + * Generates parameters for DibiTranslator. + * @param string clause name + * @return array + */ + protected function _export($clause = NULL) + { + if ($clause === NULL) { + $data = $this->clauses; + + } else { + $clause = self::_clause($clause); + if (array_key_exists($clause, $this->clauses)) { + $data = array($clause => $this->clauses[$clause]); + } else { + return array(); + } + } + + $args = array(); + foreach ($data as $clause => $statement) { + if ($statement !== NULL) { + if ($clause[0] !== '%') { + $args[] = $clause; + if ($clause === $this->command) { + $args[] = implode(' ', array_keys($this->flags)); + } + } + array_splice($args, count($args), 0, $statement); + } + } + return $args; + } + + + + /** + * Format camelCase clause name to UPPER CASE. + * @param string + * @return string + */ + private static function _clause($s) + { + if ($s === 'order' || $s === 'group') { + $s .= 'By'; + trigger_error("Did you mean '$s'?", E_USER_NOTICE); + } + return strtoupper(preg_replace('#[A-Z]#', ' $0', $s)); + + } + + + + /** + * Returns (highlighted) SQL query. + * @return string + */ + final public function __toString() + { + ob_start(); + $this->test(); + return ob_get_clean(); + } + +} + + +// PHP < 5.2 compatibility +if (!function_exists('array_fill_keys')) { + function array_fill_keys($keys, $value) + { + return array_combine($keys, array_fill(0, count($keys), $value)); + } +} diff --git a/dibi/libs/DibiLogger.php b/dibi/libs/DibiLogger.php index c864cb92..a72aa81b 100644 --- a/dibi/libs/DibiLogger.php +++ b/dibi/libs/DibiLogger.php @@ -1,106 +1,106 @@ -file = $file; - } - - - - /** - * Event handler (events: exception, connected, beforeQuery, afterQuery, begin, commit, rollback). - * - * @param DibiConnection - * @param string event name - * @param mixed - * @return void - */ - public function handler($connection, $event, $arg) - { - if ($event === 'afterQuery' && $this->logQueries) { - $this->write( - "OK: " . dibi::$sql - . ($arg instanceof DibiResult ? ";\n-- rows: " . count($arg) : '') - . "\n-- takes: " . sprintf('%0.3f', dibi::$elapsedTime * 1000) . ' ms' - . "\n-- driver: " . $connection->getConfig('driver') - . "\n-- " . date('Y-m-d H:i:s') - . "\n\n" - ); - return; - } - - if ($event === 'exception' && $this->logErrors) { - // $arg is DibiDriverException - $message = $arg->getMessage(); - $code = $arg->getCode(); - if ($code) { - $message = "[$code] $message"; - } - - $this->write( - "ERROR: $message" - . "\n-- SQL: " . dibi::$sql - . "\n-- driver: " //. $connection->getConfig('driver') - . ";\n-- " . date('Y-m-d H:i:s') - . "\n\n" - ); - return; - } - } - - - - private function write($message) - { - $handle = fopen($this->file, 'a'); - if (!$handle) return; // or throw exception? - - flock($handle, LOCK_EX); - fwrite($handle, $message); - fclose($handle); - } - -} +file = $file; + } + + + + /** + * Event handler (events: exception, connected, beforeQuery, afterQuery, begin, commit, rollback). + * + * @param DibiConnection + * @param string event name + * @param mixed + * @return void + */ + public function handler($connection, $event, $arg) + { + if ($event === 'afterQuery' && $this->logQueries) { + $this->write( + "OK: " . dibi::$sql + . ($arg instanceof DibiResult ? ";\n-- rows: " . count($arg) : '') + . "\n-- takes: " . sprintf('%0.3f', dibi::$elapsedTime * 1000) . ' ms' + . "\n-- driver: " . $connection->getConfig('driver') + . "\n-- " . date('Y-m-d H:i:s') + . "\n\n" + ); + return; + } + + if ($event === 'exception' && $this->logErrors) { + // $arg is DibiDriverException + $message = $arg->getMessage(); + $code = $arg->getCode(); + if ($code) { + $message = "[$code] $message"; + } + + $this->write( + "ERROR: $message" + . "\n-- SQL: " . dibi::$sql + . "\n-- driver: " //. $connection->getConfig('driver') + . ";\n-- " . date('Y-m-d H:i:s') + . "\n\n" + ); + return; + } + } + + + + private function write($message) + { + $handle = fopen($this->file, 'a'); + if (!$handle) return; // or throw exception? + + flock($handle, LOCK_EX); + fwrite($handle, $message); + fclose($handle); + } + +} diff --git a/dibi/libs/DibiResult.php b/dibi/libs/DibiResult.php index 1696712f..3d241fc8 100644 --- a/dibi/libs/DibiResult.php +++ b/dibi/libs/DibiResult.php @@ -1,661 +1,661 @@ - - * $result = dibi::query('SELECT * FROM [table]'); - * - * $row = $result->fetch(); - * $obj = $result->fetch(TRUE); - * $value = $result->fetchSingle(); - * $table = $result->fetchAll(); - * $pairs = $result->fetchPairs(); - * $assoc = $result->fetchAssoc('id'); - * $assoc = $result->fetchAssoc('active,#,id'); - * - * unset($result); - * - * - * @author David Grudl - * @copyright Copyright (c) 2005, 2008 David Grudl - * @package dibi - * @version $Revision$ $Date$ - */ -class DibiResult extends /*Nette::*/Object implements IDataSource -{ - /** - * IDibiDriver. - * @var array - */ - private $driver; - - /** - * Translate table. - * @var array - */ - private $xlat; - - /** - * Cache for $driver->getColumnsMeta(). - * @var array - */ - private $metaCache; - - /** - * Already fetched? Used for allowance for first seek(0). - * @var bool - */ - private $fetched = FALSE; - - /** - * Qualifiy each column name with the table name? - * @var array|FALSE - */ - private $withTables = FALSE; - - /** - * Fetch as objects or arrays? - * @var mixed TRUE | FALSE | class name - */ - private $objects = FALSE; - - - - /** - * @param IDibiDriver - * @param array - */ - public function __construct($driver, $config) - { - $this->driver = $driver; - - if (!empty($config['result:withtables'])) { - $this->setWithTables(TRUE); - } - - if (isset($config['result:objects'])) { - $this->setObjects($config['result:objects']); - } - } - - - - /** - * Automatically frees the resources allocated for this result set. - * - * @return void - */ - public function __destruct() - { - @$this->free(); // intentionally @ - } - - - - /** - * Returns the result set resource. - * - * @return mixed - */ - final public function getResource() - { - return $this->getDriver()->getResultResource(); - } - - - - /** - * Moves cursor position without fetching row. - * - * @param int the 0-based cursor pos to seek to - * @return boolean TRUE on success, FALSE if unable to seek to specified record - * @throws DibiException - */ - final public function seek($row) - { - return ($row !== 0 || $this->fetched) ? (bool) $this->getDriver()->seek($row) : TRUE; - } - - - - /** - * Returns the number of rows in a result set. - * - * @return int - */ - final public function rowCount() - { - return $this->getDriver()->rowCount(); - } - - - - /** - * Frees the resources allocated for this result set. - * - * @return void - */ - final public function free() - { - if ($this->driver !== NULL) { - $this->driver->free(); - $this->driver = NULL; - } - } - - - - /** - * Qualifiy each column name with the table name? - * - * @param bool - * @return void - * @throws DibiException - */ - final public function setWithTables($val) - { - if ($val) { - if ($this->metaCache === NULL) { - $this->metaCache = $this->getDriver()->getColumnsMeta(); - } - - $cols = array(); - foreach ($this->metaCache as $col) { - // intentional == - $name = $col['table'] == '' ? $col['name'] : ($col['table'] . '.' . $col['name']); - if (isset($cols[$name])) { - $fix = 1; - while (isset($cols[$name . '#' . $fix])) $fix++; - $name .= '#' . $fix; - } - $cols[$name] = TRUE; - } - $this->withTables = array_keys($cols); - - } else { - $this->withTables = FALSE; - } - } - - - - /** - * Qualifiy each key with the table name? - * - * @return bool - */ - final public function getWithTables() - { - return (bool) $this->withTables; - } - - - - /** - * Returns rows as arrays or objects? - * - * @param mixed TRUE | FALSE | class name - * @return void - */ - public function setObjects($type) - { - $this->objects = $type; - } - - - - /** - * Returns rows as arrays or objects? - * - * @return mixed TRUE | FALSE | class name - */ - public function getObjects() - { - return $this->objects; - } - - - - /** - * Fetches the row at current position, process optional type conversion. - * and moves the internal cursor to the next position - * - * @param mixed fetch as object? Overrides $this->setObjects() - * @return array|FALSE array on success, FALSE if no next record - */ - final public function fetch($objects = NULL) - { - if ($this->withTables === FALSE) { - $row = $this->getDriver()->fetch(TRUE); - if (!is_array($row)) return FALSE; - - } else { - $row = $this->getDriver()->fetch(FALSE); - if (!is_array($row)) return FALSE; - $row = array_combine($this->withTables, $row); - } - - $this->fetched = TRUE; - - // types-converting? - if ($this->xlat !== NULL) { - foreach ($this->xlat as $col => $type) { - if (isset($row[$col])) { - $row[$col] = $this->convert($row[$col], $type[0], $type[1]); - } - } - } - - if ($objects === NULL) { - $objects = $this->objects; - } - - if ($objects) { - if ($objects === TRUE) { - $row = (object) $row; - } else { - $row = new $objects($row); - } - } - - return $row; - } - - - - /** - * Like fetch(), but returns only first field. - * - * @return mixed value on success, FALSE if no next record - */ - final function fetchSingle() - { - $row = $this->getDriver()->fetch(TRUE); - if (!is_array($row)) return FALSE; - $this->fetched = TRUE; - $value = reset($row); - - // types-converting? - $key = key($row); - if (isset($this->xlat[$key])) { - $type = $this->xlat[$key]; - return $this->convert($value, $type[0], $type[1]); - } - - return $value; - } - - - - /** - * Fetches all records from table. - * - * @param int offset - * @param int limit - * @param bool simplify one-column result set? - * @return array - */ - final function fetchAll($offset = NULL, $limit = NULL, $simplify = TRUE) - { - $limit = $limit === NULL ? -1 : (int) $limit; - $this->seek((int) $offset); - $row = $this->fetch(); - if (!$row) return array(); // empty result set - - $data = array(); - if ($simplify && !$this->objects && count($row) === 1) { - // special case: one-column result set - $key = key($row); - do { - if ($limit === 0) break; - $limit--; - $data[] = $row[$key]; - } while ($row = $this->fetch()); - - } else { - do { - if ($limit === 0) break; - $limit--; - $data[] = $row; - } while ($row = $this->fetch()); - } - - return $data; - } - - - - /** - * Fetches all records from table and returns associative tree. - * Associative descriptor: assoc1,#,assoc2,=,assoc3,@ - * builds a tree: $data[assoc1][index][assoc2]['assoc3']->value = {record} - * - * @param string associative descriptor - * @return array - * @throws InvalidArgumentException - */ - final function fetchAssoc($assoc) - { - $this->seek(0); - $row = $this->fetch(FALSE); - if (!$row) return array(); // empty result set - - $data = NULL; - $assoc = explode(',', $assoc); - - // check columns - foreach ($assoc as $as) { - if ($as !== '#' && $as !== '=' && $as !== '@' && !array_key_exists($as, $row)) { - throw new InvalidArgumentException("Unknown column '$as' in associative descriptor."); - } - } - - // strip leading = and @ - $assoc[] = '='; // gap - $last = count($assoc) - 1; - while ($assoc[$last] === '=' || $assoc[$last] === '@') { - $leaf = $assoc[$last]; - unset($assoc[$last]); - $last--; - - if ($last < 0) { - $assoc[] = '#'; - break; - } - } - - // make associative tree - do { - $x = & $data; - - // iterative deepening - foreach ($assoc as $i => $as) { - if ($as === '#') { // indexed-array node - $x = & $x[]; - - } elseif ($as === '=') { // "record" node - if ($x === NULL) { - $x = $row; - $x = & $x[ $assoc[$i+1] ]; - $x = NULL; // prepare child node - } else { - $x = & $x[ $assoc[$i+1] ]; - } - - } elseif ($as === '@') { // "object" node - if ($x === NULL) { - $x = (object) $row; - $x = & $x->{$assoc[$i+1]}; - $x = NULL; // prepare child node - } else { - $x = & $x->{$assoc[$i+1]}; - } - - - } else { // associative-array node - $x = & $x[ $row[ $as ] ]; - } - } - - if ($x === NULL) { // build leaf - if ($leaf === '=') $x = $row; else $x = (object) $row; - } - - } while ($row = $this->fetch(FALSE)); - - unset($x); - return $data; - } - - - - /** - * Fetches all records from table like $key => $value pairs. - * - * @param string associative key - * @param string value - * @return array - * @throws InvalidArgumentException - */ - final function fetchPairs($key = NULL, $value = NULL) - { - $this->seek(0); - $row = $this->fetch(FALSE); - if (!$row) return array(); // empty result set - - $data = array(); - - if ($value === NULL) { - if ($key !== NULL) { - throw new InvalidArgumentException("Either none or both columns must be specified."); - } - - if (count($row) < 2) { - throw new UnexpectedValueException("Result must have at least two columns."); - } - - // autodetect - $tmp = array_keys($row); - $key = $tmp[0]; - $value = $tmp[1]; - - } else { - if (!array_key_exists($value, $row)) { - throw new InvalidArgumentException("Unknown value column '$value'."); - } - - if ($key === NULL) { // indexed-array - do { - $data[] = $row[$value]; - } while ($row = $this->fetch(FALSE)); - return $data; - } - - if (!array_key_exists($key, $row)) { - throw new InvalidArgumentException("Unknown key column '$key'."); - } - } - - do { - $data[ $row[$key] ] = $row[$value]; - } while ($row = $this->fetch(FALSE)); - - return $data; - } - - - - /** - * Define column type. - * @param string column - * @param string type (use constant Dibi::FIELD_*) - * @param string optional format - * @return void - */ - final public function setType($col, $type, $format = NULL) - { - $this->xlat[$col] = array($type, $format); - } - - - - /** - * Define multiple columns types (for internal usage). - * @param array - * @return void - */ - final public function setTypes(array $types) - { - $this->xlat = $types; - } - - - - /** - * Returns column type. - * @return array ($type, $format) - */ - final public function getType($col) - { - return isset($this->xlat[$col]) ? $this->xlat[$col] : NULL; - } - - - - /** - * Converts value to specified type and format - * @return array ($type, $format) - */ - final public function convert($value, $type, $format = NULL) - { - if ($value === NULL || $value === FALSE) { - return $value; - } - - switch ($type) { - case dibi::FIELD_TEXT: - return (string) $value; - - case dibi::FIELD_BINARY: - return $this->getDriver()->unescape($value, $type); - - case dibi::FIELD_INTEGER: - return (int) $value; - - case dibi::FIELD_FLOAT: - return (float) $value; - - case dibi::FIELD_DATE: - case dibi::FIELD_DATETIME: - $value = strtotime($value); - return $format === NULL ? $value : date($format, $value); - - case dibi::FIELD_BOOL: - return ((bool) $value) && $value !== 'f' && $value !== 'F'; - - default: - return $value; - } - } - - - - /** - * Gets an array of meta informations about column. - * - * @return array - */ - final public function getColumnsMeta() - { - if ($this->metaCache === NULL) { - $this->metaCache = $this->getDriver()->getColumnsMeta(); - } - - $cols = array(); - foreach ($this->metaCache as $col) { - $name = (!$this->withTables || $col['table'] === NULL) ? $col['name'] : ($col['table'] . '.' . $col['name']); - $cols[$name] = $col; - } - return $cols; - } - - - - /** - * Displays complete result-set as HTML table for debug purposes. - * - * @return void - */ - final public function dump() - { - $none = TRUE; - foreach ($this as $i => $row) { - if ($none) { - echo "\n\n\n\t\n\t\t\n"; - - foreach ($row as $col => $foo) { - echo "\t\t\n"; - } - - echo "\t\n\n\n"; - $none = FALSE; - } - - echo "\t\n\t\t\n"; - foreach ($row as $col) { - //if (is_object($col)) $col = $col->__toString(); - echo "\t\t\n"; - } - echo "\t\n"; - } - - if ($none) { - echo '

empty result set

'; - } else { - echo "\n
#row" . htmlSpecialChars($col) . "
", $i, "", htmlSpecialChars($col), "
\n"; - } - } - - - - /** - * Required by the IteratorAggregate interface. - * @param int offset - * @param int limit - * @return ArrayIterator - */ - final public function getIterator($offset = NULL, $limit = NULL) - { - return new ArrayIterator($this->fetchAll($offset, $limit, FALSE)); - } - - - - /** - * Required by the Countable interface. - * @return int - */ - final public function count() - { - return $this->rowCount(); - } - - - - /** - * Safe access to property $driver. - * - * @return IDibiDriver - * @throws InvalidStateException - */ - private function getDriver() - { - if ($this->driver === NULL) { - throw new InvalidStateException('Resultset was released from memory.'); - } - - return $this->driver; - } - - -} + + * $result = dibi::query('SELECT * FROM [table]'); + * + * $row = $result->fetch(); + * $obj = $result->fetch(TRUE); + * $value = $result->fetchSingle(); + * $table = $result->fetchAll(); + * $pairs = $result->fetchPairs(); + * $assoc = $result->fetchAssoc('id'); + * $assoc = $result->fetchAssoc('active,#,id'); + * + * unset($result); + * + * + * @author David Grudl + * @copyright Copyright (c) 2005, 2008 David Grudl + * @package dibi + */ +class DibiResult extends /*Nette::*/Object implements IDataSource +{ + /** + * IDibiDriver. + * @var array + */ + private $driver; + + /** + * Translate table. + * @var array + */ + private $xlat; + + /** + * Cache for $driver->getColumnsMeta(). + * @var array + */ + private $metaCache; + + /** + * Already fetched? Used for allowance for first seek(0). + * @var bool + */ + private $fetched = FALSE; + + /** + * Qualifiy each column name with the table name? + * @var array|FALSE + */ + private $withTables = FALSE; + + /** + * Fetch as objects or arrays? + * @var mixed TRUE | FALSE | class name + */ + private $objects = FALSE; + + + + /** + * @param IDibiDriver + * @param array + */ + public function __construct($driver, $config) + { + $this->driver = $driver; + + if (!empty($config['result:withtables'])) { + $this->setWithTables(TRUE); + } + + if (isset($config['result:objects'])) { + $this->setObjects($config['result:objects']); + } + } + + + + /** + * Automatically frees the resources allocated for this result set. + * + * @return void + */ + public function __destruct() + { + @$this->free(); // intentionally @ + } + + + + /** + * Returns the result set resource. + * + * @return mixed + */ + final public function getResource() + { + return $this->getDriver()->getResultResource(); + } + + + + /** + * Moves cursor position without fetching row. + * + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + final public function seek($row) + { + return ($row !== 0 || $this->fetched) ? (bool) $this->getDriver()->seek($row) : TRUE; + } + + + + /** + * Returns the number of rows in a result set. + * + * @return int + */ + final public function rowCount() + { + return $this->getDriver()->rowCount(); + } + + + + /** + * Frees the resources allocated for this result set. + * + * @return void + */ + final public function free() + { + if ($this->driver !== NULL) { + $this->driver->free(); + $this->driver = NULL; + } + } + + + + /** + * Qualifiy each column name with the table name? + * + * @param bool + * @return void + * @throws DibiException + */ + final public function setWithTables($val) + { + if ($val) { + if ($this->metaCache === NULL) { + $this->metaCache = $this->getDriver()->getColumnsMeta(); + } + + $cols = array(); + foreach ($this->metaCache as $col) { + // intentional == + $name = $col['table'] == '' ? $col['name'] : ($col['table'] . '.' . $col['name']); + if (isset($cols[$name])) { + $fix = 1; + while (isset($cols[$name . '#' . $fix])) $fix++; + $name .= '#' . $fix; + } + $cols[$name] = TRUE; + } + $this->withTables = array_keys($cols); + + } else { + $this->withTables = FALSE; + } + } + + + + /** + * Qualifiy each key with the table name? + * + * @return bool + */ + final public function getWithTables() + { + return (bool) $this->withTables; + } + + + + /** + * Returns rows as arrays or objects? + * + * @param mixed TRUE | FALSE | class name + * @return void + */ + public function setObjects($type) + { + $this->objects = $type; + } + + + + /** + * Returns rows as arrays or objects? + * + * @return mixed TRUE | FALSE | class name + */ + public function getObjects() + { + return $this->objects; + } + + + + /** + * Fetches the row at current position, process optional type conversion. + * and moves the internal cursor to the next position + * + * @param mixed fetch as object? Overrides $this->setObjects() + * @return array|FALSE array on success, FALSE if no next record + */ + final public function fetch($objects = NULL) + { + if ($this->withTables === FALSE) { + $row = $this->getDriver()->fetch(TRUE); + if (!is_array($row)) return FALSE; + + } else { + $row = $this->getDriver()->fetch(FALSE); + if (!is_array($row)) return FALSE; + $row = array_combine($this->withTables, $row); + } + + $this->fetched = TRUE; + + // types-converting? + if ($this->xlat !== NULL) { + foreach ($this->xlat as $col => $type) { + if (isset($row[$col])) { + $row[$col] = $this->convert($row[$col], $type[0], $type[1]); + } + } + } + + if ($objects === NULL) { + $objects = $this->objects; + } + + if ($objects) { + if ($objects === TRUE) { + $row = (object) $row; + } else { + $row = new $objects($row); + } + } + + return $row; + } + + + + /** + * Like fetch(), but returns only first field. + * + * @return mixed value on success, FALSE if no next record + */ + final function fetchSingle() + { + $row = $this->getDriver()->fetch(TRUE); + if (!is_array($row)) return FALSE; + $this->fetched = TRUE; + $value = reset($row); + + // types-converting? + $key = key($row); + if (isset($this->xlat[$key])) { + $type = $this->xlat[$key]; + return $this->convert($value, $type[0], $type[1]); + } + + return $value; + } + + + + /** + * Fetches all records from table. + * + * @param int offset + * @param int limit + * @param bool simplify one-column result set? + * @return array + */ + final function fetchAll($offset = NULL, $limit = NULL, $simplify = TRUE) + { + $limit = $limit === NULL ? -1 : (int) $limit; + $this->seek((int) $offset); + $row = $this->fetch(); + if (!$row) return array(); // empty result set + + $data = array(); + if ($simplify && !$this->objects && count($row) === 1) { + // special case: one-column result set + $key = key($row); + do { + if ($limit === 0) break; + $limit--; + $data[] = $row[$key]; + } while ($row = $this->fetch()); + + } else { + do { + if ($limit === 0) break; + $limit--; + $data[] = $row; + } while ($row = $this->fetch()); + } + + return $data; + } + + + + /** + * Fetches all records from table and returns associative tree. + * Associative descriptor: assoc1,#,assoc2,=,assoc3,@ + * builds a tree: $data[assoc1][index][assoc2]['assoc3']->value = {record} + * + * @param string associative descriptor + * @return array + * @throws InvalidArgumentException + */ + final function fetchAssoc($assoc) + { + $this->seek(0); + $row = $this->fetch(FALSE); + if (!$row) return array(); // empty result set + + $data = NULL; + $assoc = explode(',', $assoc); + + // check columns + foreach ($assoc as $as) { + if ($as !== '#' && $as !== '=' && $as !== '@' && !array_key_exists($as, $row)) { + throw new InvalidArgumentException("Unknown column '$as' in associative descriptor."); + } + } + + // strip leading = and @ + $assoc[] = '='; // gap + $last = count($assoc) - 1; + while ($assoc[$last] === '=' || $assoc[$last] === '@') { + $leaf = $assoc[$last]; + unset($assoc[$last]); + $last--; + + if ($last < 0) { + $assoc[] = '#'; + break; + } + } + + // make associative tree + do { + $x = & $data; + + // iterative deepening + foreach ($assoc as $i => $as) { + if ($as === '#') { // indexed-array node + $x = & $x[]; + + } elseif ($as === '=') { // "record" node + if ($x === NULL) { + $x = $row; + $x = & $x[ $assoc[$i+1] ]; + $x = NULL; // prepare child node + } else { + $x = & $x[ $assoc[$i+1] ]; + } + + } elseif ($as === '@') { // "object" node + if ($x === NULL) { + $x = (object) $row; + $x = & $x->{$assoc[$i+1]}; + $x = NULL; // prepare child node + } else { + $x = & $x->{$assoc[$i+1]}; + } + + + } else { // associative-array node + $x = & $x[ $row[ $as ] ]; + } + } + + if ($x === NULL) { // build leaf + if ($leaf === '=') $x = $row; else $x = (object) $row; + } + + } while ($row = $this->fetch(FALSE)); + + unset($x); + return $data; + } + + + + /** + * Fetches all records from table like $key => $value pairs. + * + * @param string associative key + * @param string value + * @return array + * @throws InvalidArgumentException + */ + final function fetchPairs($key = NULL, $value = NULL) + { + $this->seek(0); + $row = $this->fetch(FALSE); + if (!$row) return array(); // empty result set + + $data = array(); + + if ($value === NULL) { + if ($key !== NULL) { + throw new InvalidArgumentException("Either none or both columns must be specified."); + } + + if (count($row) < 2) { + throw new UnexpectedValueException("Result must have at least two columns."); + } + + // autodetect + $tmp = array_keys($row); + $key = $tmp[0]; + $value = $tmp[1]; + + } else { + if (!array_key_exists($value, $row)) { + throw new InvalidArgumentException("Unknown value column '$value'."); + } + + if ($key === NULL) { // indexed-array + do { + $data[] = $row[$value]; + } while ($row = $this->fetch(FALSE)); + return $data; + } + + if (!array_key_exists($key, $row)) { + throw new InvalidArgumentException("Unknown key column '$key'."); + } + } + + do { + $data[ $row[$key] ] = $row[$value]; + } while ($row = $this->fetch(FALSE)); + + return $data; + } + + + + /** + * Define column type. + * @param string column + * @param string type (use constant Dibi::FIELD_*) + * @param string optional format + * @return void + */ + final public function setType($col, $type, $format = NULL) + { + $this->xlat[$col] = array($type, $format); + } + + + + /** + * Define multiple columns types (for internal usage). + * @param array + * @return void + */ + final public function setTypes(array $types) + { + $this->xlat = $types; + } + + + + /** + * Returns column type. + * @return array ($type, $format) + */ + final public function getType($col) + { + return isset($this->xlat[$col]) ? $this->xlat[$col] : NULL; + } + + + + /** + * Converts value to specified type and format + * @return array ($type, $format) + */ + final public function convert($value, $type, $format = NULL) + { + if ($value === NULL || $value === FALSE) { + return $value; + } + + switch ($type) { + case dibi::FIELD_TEXT: + return (string) $value; + + case dibi::FIELD_BINARY: + return $this->getDriver()->unescape($value, $type); + + case dibi::FIELD_INTEGER: + return (int) $value; + + case dibi::FIELD_FLOAT: + return (float) $value; + + case dibi::FIELD_DATE: + case dibi::FIELD_DATETIME: + $value = strtotime($value); + return $format === NULL ? $value : date($format, $value); + + case dibi::FIELD_BOOL: + return ((bool) $value) && $value !== 'f' && $value !== 'F'; + + default: + return $value; + } + } + + + + /** + * Gets an array of meta informations about column. + * + * @return array + */ + final public function getColumnsMeta() + { + if ($this->metaCache === NULL) { + $this->metaCache = $this->getDriver()->getColumnsMeta(); + } + + $cols = array(); + foreach ($this->metaCache as $col) { + $name = (!$this->withTables || $col['table'] === NULL) ? $col['name'] : ($col['table'] . '.' . $col['name']); + $cols[$name] = $col; + } + return $cols; + } + + + + /** + * Displays complete result-set as HTML table for debug purposes. + * + * @return void + */ + final public function dump() + { + $none = TRUE; + foreach ($this as $i => $row) { + if ($none) { + echo "\n\n\n\t\n\t\t\n"; + + foreach ($row as $col => $foo) { + echo "\t\t\n"; + } + + echo "\t\n\n\n"; + $none = FALSE; + } + + echo "\t\n\t\t\n"; + foreach ($row as $col) { + //if (is_object($col)) $col = $col->__toString(); + echo "\t\t\n"; + } + echo "\t\n"; + } + + if ($none) { + echo '

empty result set

'; + } else { + echo "\n
#row" . htmlSpecialChars($col) . "
", $i, "", htmlSpecialChars($col), "
\n"; + } + } + + + + /** + * Required by the IteratorAggregate interface. + * @param int offset + * @param int limit + * @return ArrayIterator + */ + final public function getIterator($offset = NULL, $limit = NULL) + { + return new ArrayIterator($this->fetchAll($offset, $limit, FALSE)); + } + + + + /** + * Required by the Countable interface. + * @return int + */ + final public function count() + { + return $this->rowCount(); + } + + + + /** + * Safe access to property $driver. + * + * @return IDibiDriver + * @throws InvalidStateException + */ + private function getDriver() + { + if ($this->driver === NULL) { + throw new InvalidStateException('Resultset was released from memory.'); + } + + return $this->driver; + } + + +} diff --git a/dibi/libs/DibiTable.php b/dibi/libs/DibiTable.php index eaee3fcd..fc116527 100644 --- a/dibi/libs/DibiTable.php +++ b/dibi/libs/DibiTable.php @@ -1,290 +1,290 @@ -connection = $connection === NULL ? dibi::getConnection() : $connection; - $this->setup(); - } - - - - /** - * Returns the table name. - * @return string - */ - public function getName() - { - return $this->name; - } - - - - /** - * Returns the primary key name. - * @return string - */ - public function getPrimary() - { - return $this->primary; - } - - - - /** - * Returns the dibi connection. - * @return DibiConnection - */ - public function getConnection() - { - return $this->connection; - } - - - - /** - * Setup object. - * @return void - */ - protected function setup() - { - // autodetect table name - if ($this->name === NULL) { - $name = $this->getClass(); - if (FALSE !== ($pos = strrpos($name, ':'))) { - $name = substr($name, $pos + 1); - } - if (self::$lowerCase) { - $name = strtolower($name); - } - $this->name = $name; - } - - // autodetect primary key name - if ($this->primary === NULL) { - $this->primary = str_replace( - array('%p', '%s'), - array($this->name, trim($this->name, 's')), // the simplest inflector in the world :-)) - self::$primaryMask - ); - } - } - - - - /** - * Inserts row into a table. - * @param array|object - * @return int new primary key - */ - public function insert($data) - { - $this->connection->query( - 'INSERT INTO %n', $this->name, '%v', $this->prepare($data) - ); - return $this->connection->insertId(); - } - - - - /** - * Updates rows in a table. - * @param mixed primary key value(s) - * @param array|object - * @return int number of updated rows - */ - public function update($where, $data) - { - $this->connection->query( - 'UPDATE %n', $this->name, - 'SET %a', $this->prepare($data), - 'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $where, ')' - ); - return $this->connection->affectedRows(); - } - - - - /** - * Deletes rows from a table by primary key. - * @param mixed primary key value(s) - * @return int number of deleted rows - */ - public function delete($where) - { - $this->connection->query( - 'DELETE FROM %n', $this->name, - 'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $where, ')' - ); - return $this->connection->affectedRows(); - } - - - - /** - * Finds rows by primary key. - * @param mixed primary key value(s) - * @return DibiResult - */ - public function find($what) - { - if (!is_array($what)) { - $what = func_get_args(); - } - return $this->complete($this->connection->query( - 'SELECT * FROM %n', $this->name, - 'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $what, ')' - )); - } - - - - /** - * Selects all rows. - * @param string column to order by - * @return DibiResult - */ - public function findAll($order = NULL) - { - if ($order === NULL) { - return $this->complete($this->connection->query( - 'SELECT * FROM %n', $this->name - )); - } else { - $order = func_get_args(); - return $this->complete($this->connection->query( - 'SELECT * FROM %n', $this->name, - 'ORDER BY %n', $order - )); - } - } - - - - /** - * Fetches single row. - * @param scalar|array primary key value - * @return array|object row - */ - public function fetch($conditions) - { - if (is_array($conditions)) { - return $this->complete($this->connection->query( - 'SELECT * FROM %n', $this->name, - 'WHERE %and', $conditions - ))->fetch(); - } - return $this->complete($this->connection->query( - 'SELECT * FROM %n', $this->name, - 'WHERE %n=' . $this->primaryModifier, $this->primary, $conditions - ))->fetch(); - } - - - - /** - * Returns a blank row (not fetched from database). - * @return array|object - */ - public function createBlank() - { - $row = $this->blankRow; - $row[$this->primary] = NULL; - - if ($class = $this->connection->getConfig('result:objects')) { - if ($class === TRUE) { - $row = (object) $row; - } else { - $row = new $class($row); - } - } - - return $row; - } - - - - /** - * User data pre-processing. - * @param array|object - * @return array - */ - protected function prepare($data) - { - if (is_object($data)) { - return (array) $data; - } elseif (is_array($data)) { - return $data; - } - - throw new InvalidArgumentException('Dataset must be array or anonymous object.'); - } - - - - /** - * User DibiResult post-processing. - * @param DibiResult - * @return DibiResult - */ - protected function complete($res) - { - return $res; - } - -} +connection = $connection === NULL ? dibi::getConnection() : $connection; + $this->setup(); + } + + + + /** + * Returns the table name. + * @return string + */ + public function getName() + { + return $this->name; + } + + + + /** + * Returns the primary key name. + * @return string + */ + public function getPrimary() + { + return $this->primary; + } + + + + /** + * Returns the dibi connection. + * @return DibiConnection + */ + public function getConnection() + { + return $this->connection; + } + + + + /** + * Setup object. + * @return void + */ + protected function setup() + { + // autodetect table name + if ($this->name === NULL) { + $name = $this->getClass(); + if (FALSE !== ($pos = strrpos($name, ':'))) { + $name = substr($name, $pos + 1); + } + if (self::$lowerCase) { + $name = strtolower($name); + } + $this->name = $name; + } + + // autodetect primary key name + if ($this->primary === NULL) { + $this->primary = str_replace( + array('%p', '%s'), + array($this->name, trim($this->name, 's')), // the simplest inflector in the world :-)) + self::$primaryMask + ); + } + } + + + + /** + * Inserts row into a table. + * @param array|object + * @return int new primary key + */ + public function insert($data) + { + $this->connection->query( + 'INSERT INTO %n', $this->name, '%v', $this->prepare($data) + ); + return $this->connection->insertId(); + } + + + + /** + * Updates rows in a table. + * @param mixed primary key value(s) + * @param array|object + * @return int number of updated rows + */ + public function update($where, $data) + { + $this->connection->query( + 'UPDATE %n', $this->name, + 'SET %a', $this->prepare($data), + 'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $where, ')' + ); + return $this->connection->affectedRows(); + } + + + + /** + * Deletes rows from a table by primary key. + * @param mixed primary key value(s) + * @return int number of deleted rows + */ + public function delete($where) + { + $this->connection->query( + 'DELETE FROM %n', $this->name, + 'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $where, ')' + ); + return $this->connection->affectedRows(); + } + + + + /** + * Finds rows by primary key. + * @param mixed primary key value(s) + * @return DibiResult + */ + public function find($what) + { + if (!is_array($what)) { + $what = func_get_args(); + } + return $this->complete($this->connection->query( + 'SELECT * FROM %n', $this->name, + 'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $what, ')' + )); + } + + + + /** + * Selects all rows. + * @param string column to order by + * @return DibiResult + */ + public function findAll($order = NULL) + { + if ($order === NULL) { + return $this->complete($this->connection->query( + 'SELECT * FROM %n', $this->name + )); + } else { + $order = func_get_args(); + return $this->complete($this->connection->query( + 'SELECT * FROM %n', $this->name, + 'ORDER BY %n', $order + )); + } + } + + + + /** + * Fetches single row. + * @param scalar|array primary key value + * @return array|object row + */ + public function fetch($conditions) + { + if (is_array($conditions)) { + return $this->complete($this->connection->query( + 'SELECT * FROM %n', $this->name, + 'WHERE %and', $conditions + ))->fetch(); + } + return $this->complete($this->connection->query( + 'SELECT * FROM %n', $this->name, + 'WHERE %n=' . $this->primaryModifier, $this->primary, $conditions + ))->fetch(); + } + + + + /** + * Returns a blank row (not fetched from database). + * @return array|object + */ + public function createBlank() + { + $row = $this->blankRow; + $row[$this->primary] = NULL; + + if ($class = $this->connection->getConfig('result:objects')) { + if ($class === TRUE) { + $row = (object) $row; + } else { + $row = new $class($row); + } + } + + return $row; + } + + + + /** + * User data pre-processing. + * @param array|object + * @return array + */ + protected function prepare($data) + { + if (is_object($data)) { + return (array) $data; + } elseif (is_array($data)) { + return $data; + } + + throw new InvalidArgumentException('Dataset must be array or anonymous object.'); + } + + + + /** + * User DibiResult post-processing. + * @param DibiResult + * @return DibiResult + */ + protected function complete($res) + { + return $res; + } + +} diff --git a/dibi/libs/DibiTranslator.php b/dibi/libs/DibiTranslator.php index 65c6821d..9a3bab06 100644 --- a/dibi/libs/DibiTranslator.php +++ b/dibi/libs/DibiTranslator.php @@ -1,457 +1,457 @@ -driver = $driver; - } - - - - /** - * return IDibiDriver. - */ - public function getDriver() - { - return $this->driver; - } - - - - /** - * Generates SQL. - * - * @param array - * @return bool - */ - public function translate(array $args) - { - $this->limit = -1; - $this->offset = 0; - $this->hasError = FALSE; - $commandIns = NULL; - $lastArr = NULL; - // shortcuts - $cursor = & $this->cursor; - $cursor = 0; - $this->args = array_values($args); - $args = & $this->args; - - // conditional sql - $this->ifLevel = $this->ifLevelStart = 0; - $comment = & $this->comment; - $comment = FALSE; - - // iterate - $sql = array(); - while ($cursor < count($args)) - { - $arg = $args[$cursor]; - $cursor++; - - // simple string means SQL - if (is_string($arg)) { - // speed-up - is regexp required? - $toSkip = strcspn($arg, '`[\'"%'); - - if (strlen($arg) === $toSkip) { // needn't be translated - $sql[] = $arg; - } else { - $sql[] = substr($arg, 0, $toSkip) -/* - preg_replace_callback('/ - (?=`|\[|\'|"|%) ## speed-up - (?: - `(.+?)`| ## 1) `identifier` - \[(.+?)\]| ## 2) [identifier] - (\')((?:\'\'|[^\'])*)\'| ## 3,4) string - (")((?:""|[^"])*)"| ## 5,6) "string" - (\'|") ## 7) lone-quote - %([a-zA-Z]{1,4})(?![a-zA-Z])|## 8) modifier - )/xs', -*/ // note: this can change $this->args & $this->cursor & ... - . preg_replace_callback('/(?=`|\[|\'|"|%)(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|%([a-zA-Z]{1,4})(?![a-zA-Z]))/s', - array($this, 'cb'), - substr($arg, $toSkip) - ); - - } - continue; - } - - if ($comment) { - $sql[] = '...'; - continue; - } - - if (is_array($arg)) { - if (is_string(key($arg))) { - // associative array -> autoselect between SET or VALUES & LIST - if ($commandIns === NULL) { - $commandIns = strtoupper(substr(ltrim($args[0]), 0, 6)); - $commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC'; - $sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a'); - } else { - if ($lastArr === $cursor - 1) $sql[] = ','; - $sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a'); - } - $lastArr = $cursor; - continue; - - } elseif ($cursor === 1) { - // implicit array expansion - $cursor = 0; - array_splice($args, 0, 1, $arg); - continue; - } - } - - // default processing - $sql[] = $this->formatValue($arg, FALSE); - } // while - - - if ($comment) $sql[] = "*/"; - - $sql = implode(' ', $sql); - - // apply limit - if ($this->limit > -1 || $this->offset > 0) { - $this->driver->applyLimit($sql, $this->limit, $this->offset); - } - - $this->sql = $sql; - return !$this->hasError; - } - - - - /** - * Apply modifier to single value. - * @param mixed - * @param string - * @return string - */ - public function formatValue($value, $modifier) - { - // array processing (with or without modifier) - if (is_array($value)) { - - $vx = $kx = array(); - $separator = ', '; - switch ($modifier) { - case 'and': - case 'or': - $separator = ' ' . strtoupper($modifier) . ' '; - if (!is_string(key($value))) { - foreach ($value as $v) { - $vx[] = $this->formatValue($v, 'sql'); - } - return implode($separator, $vx); - } - // break intentionally omitted - case 'a': // SET key=val, key=val, ... - foreach ($value as $k => $v) { - $pair = explode('%', $k, 2); // split into identifier & modifier - $vx[] = $this->delimite($pair[0]) . '=' - . $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE); - } - return implode($separator, $vx); - - - case 'l': // LIST (val, val, ...) - foreach ($value as $k => $v) { - $pair = explode('%', $k, 2); // split into identifier & modifier - $vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE); - } - return '(' . implode(', ', $vx) . ')'; - - - case 'v': // (key, key, ...) VALUES (val, val, ...) - foreach ($value as $k => $v) { - $pair = explode('%', $k, 2); // split into identifier & modifier - $kx[] = $this->delimite($pair[0]); - $vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE); - } - return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')'; - - default: - foreach ($value as $v) { - $vx[] = $this->formatValue($v, $modifier); - } - return implode(', ', $vx); - } - } - - - // with modifier procession - if ($modifier) { - if ($value === NULL) { - return 'NULL'; - } - - if ($value instanceof IDibiVariable) { - return $value->toSql($this, $modifier); - } - - if (!is_scalar($value)) { // array is already processed - $this->hasError = TRUE; - return '**Unexpected type ' . gettype($value) . '**'; - } - - switch ($modifier) { - case 's': // string - case 'bin':// binary - case 'b': // boolean - return $this->driver->escape($value, $modifier); - - case 'sn': // string or NULL - return $value == '' ? 'NULL' : $this->driver->escape($value, dibi::FIELD_TEXT); // notice two equal signs - - case 'i': // signed int - case 'u': // unsigned int, ignored - // support for numbers - keep them unchanged - if (is_string($value) && preg_match('#[+-]?\d+(e\d+)?$#A', $value)) { - return $value; - } - return (string) (int) ($value + 0); - - case 'f': // float - // support for numbers - keep them unchanged - if (is_numeric($value) && (!is_string($value) || strpos($value, 'x') === FALSE)) { - return $value; // something like -9E-005 is accepted by SQL, HEX values is not - } - return (string) ($value + 0); - - case 'd': // date - case 't': // datetime - return $this->driver->escape(is_string($value) ? strtotime($value) : $value, $modifier); - - case 'n': // identifier name - return $this->delimite($value); - - case 'sql':// preserve as SQL - $value = (string) $value; - // speed-up - is regexp required? - $toSkip = strcspn($value, '`[\'"'); - if (strlen($value) === $toSkip) { // needn't be translated - return $value; - } else { - return substr($value, 0, $toSkip) - . preg_replace_callback('/(?=`|\[|\'|")(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"(\'|"))/s', - array($this, 'cb'), - substr($value, $toSkip) - ); - } - - case 'and': - case 'or': - case 'a': - case 'l': - case 'v': - $this->hasError = TRUE; - return '**Unexpected type ' . gettype($value) . '**'; - - default: - $this->hasError = TRUE; - return "**Unknown or invalid modifier %$modifier**"; - } - } - - - // without modifier procession - if (is_string($value)) - return $this->driver->escape($value, dibi::FIELD_TEXT); - - if (is_int($value) || is_float($value)) - return (string) $value; // something like -9E-005 is accepted by SQL - - if (is_bool($value)) - return $this->driver->escape($value, dibi::FIELD_BOOL); - - if ($value === NULL) - return 'NULL'; - - if ($value instanceof IDibiVariable) - return $value->toSql($this, NULL); - - $this->hasError = TRUE; - return '**Unexpected ' . gettype($value) . '**'; - } - - - - /** - * PREG callback from translate() or formatValue(). - * @param array - * @return string - */ - private function cb($matches) - { - // [1] => `ident` - // [2] => [ident] - // [3] => ' - // [4] => string - // [5] => " - // [6] => string - // [7] => lone-quote - // [8] => modifier (when called from self::translate()) - - if (!empty($matches[8])) { // modifier - $mod = $matches[8]; - $cursor = & $this->cursor; - - if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') { - $this->hasError = TRUE; - return "**Extra modifier %$mod**"; - } - - if ($mod === 'if') { - $this->ifLevel++; - $cursor++; - if (!$this->comment && !$this->args[$cursor - 1]) { - // open comment - $this->ifLevelStart = $this->ifLevel; - $this->comment = TRUE; - return "/*"; - } - return ''; - - } elseif ($mod === 'else') { - if ($this->ifLevelStart === $this->ifLevel) { - $this->ifLevelStart = 0; - $this->comment = FALSE; - return "*/"; - } elseif (!$this->comment) { - $this->ifLevelStart = $this->ifLevel; - $this->comment = TRUE; - return "/*"; - } - - } elseif ($mod === 'end') { - $this->ifLevel--; - if ($this->ifLevelStart === $this->ifLevel + 1) { - // close comment - $this->ifLevelStart = 0; - $this->comment = FALSE; - return "*/"; - } - return ''; - - } elseif ($mod === 'ex') { // array expansion - array_splice($this->args, $cursor, 1, $this->args[$cursor]); - return ''; - - } elseif ($mod === 'lmt') { // apply limit - if ($this->args[$cursor] !== NULL) $this->limit = (int) $this->args[$cursor]; - $cursor++; - return ''; - - } elseif ($mod === 'ofs') { // apply offset - if ($this->args[$cursor] !== NULL) $this->offset = (int) $this->args[$cursor]; - $cursor++; - return ''; - - } else { // default processing - $cursor++; - return $this->formatValue($this->args[$cursor - 1], $mod); - } - } - - if ($this->comment) return '...'; - - if ($matches[1]) // SQL identifiers: `ident` - return $this->delimite($matches[1]); - - if ($matches[2]) // SQL identifiers: [ident] - return $this->delimite($matches[2]); - - if ($matches[3]) // SQL strings: '...' - return $this->driver->escape( str_replace("''", "'", $matches[4]), dibi::FIELD_TEXT); - - if ($matches[5]) // SQL strings: "..." - return $this->driver->escape( str_replace('""', '"', $matches[6]), dibi::FIELD_TEXT); - - if ($matches[7]) { // string quote - $this->hasError = TRUE; - return '**Alone quote**'; - } - - die('this should be never executed'); - } - - - - /** - * Apply substitutions to indentifier and delimites it. - * - * @param string indentifier - * @return string - */ - private function delimite($value) - { - return $this->driver->escape(dibi::substitute($value), dibi::IDENTIFIER); - } - - -} // class DibiTranslator +driver = $driver; + } + + + + /** + * return IDibiDriver. + */ + public function getDriver() + { + return $this->driver; + } + + + + /** + * Generates SQL. + * + * @param array + * @return bool + */ + public function translate(array $args) + { + $this->limit = -1; + $this->offset = 0; + $this->hasError = FALSE; + $commandIns = NULL; + $lastArr = NULL; + // shortcuts + $cursor = & $this->cursor; + $cursor = 0; + $this->args = array_values($args); + $args = & $this->args; + + // conditional sql + $this->ifLevel = $this->ifLevelStart = 0; + $comment = & $this->comment; + $comment = FALSE; + + // iterate + $sql = array(); + while ($cursor < count($args)) + { + $arg = $args[$cursor]; + $cursor++; + + // simple string means SQL + if (is_string($arg)) { + // speed-up - is regexp required? + $toSkip = strcspn($arg, '`[\'"%'); + + if (strlen($arg) === $toSkip) { // needn't be translated + $sql[] = $arg; + } else { + $sql[] = substr($arg, 0, $toSkip) +/* + preg_replace_callback('/ + (?=`|\[|\'|"|%) ## speed-up + (?: + `(.+?)`| ## 1) `identifier` + \[(.+?)\]| ## 2) [identifier] + (\')((?:\'\'|[^\'])*)\'| ## 3,4) string + (")((?:""|[^"])*)"| ## 5,6) "string" + (\'|") ## 7) lone-quote + %([a-zA-Z]{1,4})(?![a-zA-Z])|## 8) modifier + )/xs', +*/ // note: this can change $this->args & $this->cursor & ... + . preg_replace_callback('/(?=`|\[|\'|"|%)(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|%([a-zA-Z]{1,4})(?![a-zA-Z]))/s', + array($this, 'cb'), + substr($arg, $toSkip) + ); + + } + continue; + } + + if ($comment) { + $sql[] = '...'; + continue; + } + + if (is_array($arg)) { + if (is_string(key($arg))) { + // associative array -> autoselect between SET or VALUES & LIST + if ($commandIns === NULL) { + $commandIns = strtoupper(substr(ltrim($args[0]), 0, 6)); + $commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC'; + $sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a'); + } else { + if ($lastArr === $cursor - 1) $sql[] = ','; + $sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a'); + } + $lastArr = $cursor; + continue; + + } elseif ($cursor === 1) { + // implicit array expansion + $cursor = 0; + array_splice($args, 0, 1, $arg); + continue; + } + } + + // default processing + $sql[] = $this->formatValue($arg, FALSE); + } // while + + + if ($comment) $sql[] = "*/"; + + $sql = implode(' ', $sql); + + // apply limit + if ($this->limit > -1 || $this->offset > 0) { + $this->driver->applyLimit($sql, $this->limit, $this->offset); + } + + $this->sql = $sql; + return !$this->hasError; + } + + + + /** + * Apply modifier to single value. + * @param mixed + * @param string + * @return string + */ + public function formatValue($value, $modifier) + { + // array processing (with or without modifier) + if (is_array($value)) { + + $vx = $kx = array(); + $separator = ', '; + switch ($modifier) { + case 'and': + case 'or': + $separator = ' ' . strtoupper($modifier) . ' '; + if (!is_string(key($value))) { + foreach ($value as $v) { + $vx[] = $this->formatValue($v, 'sql'); + } + return implode($separator, $vx); + } + // break intentionally omitted + case 'a': // SET key=val, key=val, ... + foreach ($value as $k => $v) { + $pair = explode('%', $k, 2); // split into identifier & modifier + $vx[] = $this->delimite($pair[0]) . '=' + . $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE); + } + return implode($separator, $vx); + + + case 'l': // LIST (val, val, ...) + foreach ($value as $k => $v) { + $pair = explode('%', $k, 2); // split into identifier & modifier + $vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE); + } + return '(' . implode(', ', $vx) . ')'; + + + case 'v': // (key, key, ...) VALUES (val, val, ...) + foreach ($value as $k => $v) { + $pair = explode('%', $k, 2); // split into identifier & modifier + $kx[] = $this->delimite($pair[0]); + $vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE); + } + return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')'; + + default: + foreach ($value as $v) { + $vx[] = $this->formatValue($v, $modifier); + } + return implode(', ', $vx); + } + } + + + // with modifier procession + if ($modifier) { + if ($value === NULL) { + return 'NULL'; + } + + if ($value instanceof IDibiVariable) { + return $value->toSql($this, $modifier); + } + + if (!is_scalar($value)) { // array is already processed + $this->hasError = TRUE; + return '**Unexpected type ' . gettype($value) . '**'; + } + + switch ($modifier) { + case 's': // string + case 'bin':// binary + case 'b': // boolean + return $this->driver->escape($value, $modifier); + + case 'sn': // string or NULL + return $value == '' ? 'NULL' : $this->driver->escape($value, dibi::FIELD_TEXT); // notice two equal signs + + case 'i': // signed int + case 'u': // unsigned int, ignored + // support for numbers - keep them unchanged + if (is_string($value) && preg_match('#[+-]?\d+(e\d+)?$#A', $value)) { + return $value; + } + return (string) (int) ($value + 0); + + case 'f': // float + // support for numbers - keep them unchanged + if (is_numeric($value) && (!is_string($value) || strpos($value, 'x') === FALSE)) { + return $value; // something like -9E-005 is accepted by SQL, HEX values is not + } + return (string) ($value + 0); + + case 'd': // date + case 't': // datetime + return $this->driver->escape(is_string($value) ? strtotime($value) : $value, $modifier); + + case 'n': // identifier name + return $this->delimite($value); + + case 'sql':// preserve as SQL + $value = (string) $value; + // speed-up - is regexp required? + $toSkip = strcspn($value, '`[\'"'); + if (strlen($value) === $toSkip) { // needn't be translated + return $value; + } else { + return substr($value, 0, $toSkip) + . preg_replace_callback('/(?=`|\[|\'|")(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"(\'|"))/s', + array($this, 'cb'), + substr($value, $toSkip) + ); + } + + case 'and': + case 'or': + case 'a': + case 'l': + case 'v': + $this->hasError = TRUE; + return '**Unexpected type ' . gettype($value) . '**'; + + default: + $this->hasError = TRUE; + return "**Unknown or invalid modifier %$modifier**"; + } + } + + + // without modifier procession + if (is_string($value)) + return $this->driver->escape($value, dibi::FIELD_TEXT); + + if (is_int($value) || is_float($value)) + return (string) $value; // something like -9E-005 is accepted by SQL + + if (is_bool($value)) + return $this->driver->escape($value, dibi::FIELD_BOOL); + + if ($value === NULL) + return 'NULL'; + + if ($value instanceof IDibiVariable) + return $value->toSql($this, NULL); + + $this->hasError = TRUE; + return '**Unexpected ' . gettype($value) . '**'; + } + + + + /** + * PREG callback from translate() or formatValue(). + * @param array + * @return string + */ + private function cb($matches) + { + // [1] => `ident` + // [2] => [ident] + // [3] => ' + // [4] => string + // [5] => " + // [6] => string + // [7] => lone-quote + // [8] => modifier (when called from self::translate()) + + if (!empty($matches[8])) { // modifier + $mod = $matches[8]; + $cursor = & $this->cursor; + + if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') { + $this->hasError = TRUE; + return "**Extra modifier %$mod**"; + } + + if ($mod === 'if') { + $this->ifLevel++; + $cursor++; + if (!$this->comment && !$this->args[$cursor - 1]) { + // open comment + $this->ifLevelStart = $this->ifLevel; + $this->comment = TRUE; + return "/*"; + } + return ''; + + } elseif ($mod === 'else') { + if ($this->ifLevelStart === $this->ifLevel) { + $this->ifLevelStart = 0; + $this->comment = FALSE; + return "*/"; + } elseif (!$this->comment) { + $this->ifLevelStart = $this->ifLevel; + $this->comment = TRUE; + return "/*"; + } + + } elseif ($mod === 'end') { + $this->ifLevel--; + if ($this->ifLevelStart === $this->ifLevel + 1) { + // close comment + $this->ifLevelStart = 0; + $this->comment = FALSE; + return "*/"; + } + return ''; + + } elseif ($mod === 'ex') { // array expansion + array_splice($this->args, $cursor, 1, $this->args[$cursor]); + return ''; + + } elseif ($mod === 'lmt') { // apply limit + if ($this->args[$cursor] !== NULL) $this->limit = (int) $this->args[$cursor]; + $cursor++; + return ''; + + } elseif ($mod === 'ofs') { // apply offset + if ($this->args[$cursor] !== NULL) $this->offset = (int) $this->args[$cursor]; + $cursor++; + return ''; + + } else { // default processing + $cursor++; + return $this->formatValue($this->args[$cursor - 1], $mod); + } + } + + if ($this->comment) return '...'; + + if ($matches[1]) // SQL identifiers: `ident` + return $this->delimite($matches[1]); + + if ($matches[2]) // SQL identifiers: [ident] + return $this->delimite($matches[2]); + + if ($matches[3]) // SQL strings: '...' + return $this->driver->escape( str_replace("''", "'", $matches[4]), dibi::FIELD_TEXT); + + if ($matches[5]) // SQL strings: "..." + return $this->driver->escape( str_replace('""', '"', $matches[6]), dibi::FIELD_TEXT); + + if ($matches[7]) { // string quote + $this->hasError = TRUE; + return '**Alone quote**'; + } + + die('this should be never executed'); + } + + + + /** + * Apply substitutions to indentifier and delimites it. + * + * @param string indentifier + * @return string + */ + private function delimite($value) + { + return $this->driver->escape(dibi::substitute($value), dibi::IDENTIFIER); + } + + +} // class DibiTranslator diff --git a/dibi/libs/DibiVariable.php b/dibi/libs/DibiVariable.php index 0620e693..00e07ad6 100644 --- a/dibi/libs/DibiVariable.php +++ b/dibi/libs/DibiVariable.php @@ -1,48 +1,49 @@ -value = $value; - $this->modifier = $modifier; - } - - - - public function toSql(DibiTranslator $translator, $modifier) - { - return $translator->formatValue($this->value, $this->modifier); - } - +value = $value; + $this->modifier = $modifier; + } + + + + public function toSql(DibiTranslator $translator, $modifier) + { + return $translator->formatValue($this->value, $this->modifier); + } + } \ No newline at end of file diff --git a/dibi/libs/interfaces.php b/dibi/libs/interfaces.php index dd4dc940..8bc8a4fe 100644 --- a/dibi/libs/interfaces.php +++ b/dibi/libs/interfaces.php @@ -1,256 +1,256 @@ -dibi apply limit/offset example -
- 'sqlite',
-	'database' => 'sample.sdb',
-));
-
-
-// no limit
-$res = dibi::query('SELECT * FROM [products]');
-foreach ($res as $n => $row) {
-	print_r($row);
-}
-
-echo '
'; - -// with limit = 2 -$res = dibi::query('SELECT * FROM [products] %lmt', 2); -foreach ($res as $n => $row) { - print_r($row); -} - -echo '
'; - -// with limit = 2, offset = 1 -$res = dibi::query('SELECT * FROM [products] %lmt %ofs', 2, 1); -foreach ($res as $n => $row) { - print_r($row); -} +

dibi apply limit/offset example

+
+ 'sqlite',
+	'database' => 'sample.sdb',
+));
+
+
+// no limit
+$res = dibi::query('SELECT * FROM [products]');
+foreach ($res as $n => $row) {
+	print_r($row);
+}
+
+echo '
'; + +// with limit = 2 +$res = dibi::query('SELECT * FROM [products] %lmt', 2); +foreach ($res as $n => $row) { + print_r($row); +} + +echo '
'; + +// with limit = 2, offset = 1 +$res = dibi::query('SELECT * FROM [products] %lmt %ofs', 2, 1); +foreach ($res as $n => $row) { + print_r($row); +} diff --git a/examples/connect.php b/examples/connect.php index 88279e12..62414272 100644 --- a/examples/connect.php +++ b/examples/connect.php @@ -1,145 +1,145 @@ -

dibi::connect() example

-Connecting to Sqlite: '; -try { - dibi::connect(array( - 'driver' => 'sqlite', - 'database' => 'sample.sdb', - 'result:objects' => TRUE, // fetch rows as objects - )); - echo 'OK'; - -} catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; -} -echo "

\n"; - - - - -// connects to MySQL using DSN -echo '

Connecting to MySQL: '; -try { - dibi::connect('driver=mysql&host=localhost&username=root&password=xxx&database=test&charset=utf8'); - echo 'OK'; - -} catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; -} -echo "

\n"; - - - - -// connects to MySQLi using array -echo '

Connecting to MySQL: '; -try { - dibi::connect(array( - 'driver' => 'mysqli', - 'host' => 'localhost', - 'username' => 'root', - 'password' => 'xxx', - 'database' => 'dibi', - 'charset' => 'utf8', - )); - echo 'OK'; - -} catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; -} -echo "

\n"; - - - - -// connects to ODBC -echo '

Connecting to ODBC: '; -try { - dibi::connect(array( - 'driver' => 'odbc', - 'username' => 'root', - 'password' => '***', - 'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.dirname(__FILE__).'/sample.mdb', - )); - echo 'OK'; - -} catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; -} -echo "

\n"; - - - - -// connects to PostgreSql -echo '

Connecting to PostgreSql: '; -try { - dibi::connect(array( - 'driver' => 'postgre', - 'string' => 'host=localhost port=5432 dbname=mary', - 'persistent' => TRUE, - )); - echo 'OK'; - -} catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; -} -echo "

\n"; - - - - -// connects to PDO -echo '

Connecting to Sqlite via PDO: '; -try { - dibi::connect(array( - 'driver' => 'pdo', - 'dsn' => 'sqlite2::memory:', - )); - echo 'OK'; - -} catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; -} -echo "

\n"; - - - -// connects to MS SQL -echo '

Connecting to MS SQL: '; -try { - dibi::connect(array( - 'driver' => 'mssql', - 'host' => 'localhost', - 'username' => 'root', - 'password' => 'xxx', - )); - echo 'OK'; - -} catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; -} -echo "

\n"; - - - -// connects to Oracle -echo '

Connecting to Oracle: '; -try { - dibi::connect(array( - 'driver' => 'oracle', - 'username' => 'root', - 'password' => 'xxx', - 'database' => 'db', - )); - echo 'OK'; - -} catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; -} +

dibi::connect() example

+Connecting to Sqlite: '; +try { + dibi::connect(array( + 'driver' => 'sqlite', + 'database' => 'sample.sdb', + 'result:objects' => TRUE, // fetch rows as objects + )); + echo 'OK'; + +} catch (DibiException $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} +echo "

\n"; + + + + +// connects to MySQL using DSN +echo '

Connecting to MySQL: '; +try { + dibi::connect('driver=mysql&host=localhost&username=root&password=xxx&database=test&charset=utf8'); + echo 'OK'; + +} catch (DibiException $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} +echo "

\n"; + + + + +// connects to MySQLi using array +echo '

Connecting to MySQL: '; +try { + dibi::connect(array( + 'driver' => 'mysqli', + 'host' => 'localhost', + 'username' => 'root', + 'password' => 'xxx', + 'database' => 'dibi', + 'charset' => 'utf8', + )); + echo 'OK'; + +} catch (DibiException $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} +echo "

\n"; + + + + +// connects to ODBC +echo '

Connecting to ODBC: '; +try { + dibi::connect(array( + 'driver' => 'odbc', + 'username' => 'root', + 'password' => '***', + 'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.dirname(__FILE__).'/sample.mdb', + )); + echo 'OK'; + +} catch (DibiException $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} +echo "

\n"; + + + + +// connects to PostgreSql +echo '

Connecting to PostgreSql: '; +try { + dibi::connect(array( + 'driver' => 'postgre', + 'string' => 'host=localhost port=5432 dbname=mary', + 'persistent' => TRUE, + )); + echo 'OK'; + +} catch (DibiException $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} +echo "

\n"; + + + + +// connects to PDO +echo '

Connecting to Sqlite via PDO: '; +try { + dibi::connect(array( + 'driver' => 'pdo', + 'dsn' => 'sqlite2::memory:', + )); + echo 'OK'; + +} catch (DibiException $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} +echo "

\n"; + + + +// connects to MS SQL +echo '

Connecting to MS SQL: '; +try { + dibi::connect(array( + 'driver' => 'mssql', + 'host' => 'localhost', + 'username' => 'root', + 'password' => 'xxx', + )); + echo 'OK'; + +} catch (DibiException $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} +echo "

\n"; + + + +// connects to Oracle +echo '

Connecting to Oracle: '; +try { + dibi::connect(array( + 'driver' => 'oracle', + 'username' => 'root', + 'password' => 'xxx', + 'database' => 'db', + )); + echo 'OK'; + +} catch (DibiException $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} echo "

\n"; \ No newline at end of file diff --git a/examples/datetime.demo.php b/examples/datetime.demo.php index 9b1535cd..978414a8 100644 --- a/examples/datetime.demo.php +++ b/examples/datetime.demo.php @@ -1,28 +1,28 @@ -

IDibiVariable example

- 'sqlite', - 'database' => 'sample.sdb', - 'format:date' => "'Y-m-d'", - 'format:datetime' => "'Y-m-d H-i-s'", -)); - - - -// generate and dump SQL -dibi::test(" -INSERT INTO [mytable]", array( - 'id' => 123, - 'date' => dibi::date('12.3.2007'), - 'stamp' => dibi::dateTime('23.1.2007 10:23'), -)); +

IDibiVariable example

+ 'sqlite', + 'database' => 'sample.sdb', + 'format:date' => "'Y-m-d'", + 'format:datetime' => "'Y-m-d H-i-s'", +)); + + + +// generate and dump SQL +dibi::test(" +INSERT INTO [mytable]", array( + 'id' => 123, + 'date' => dibi::date('12.3.2007'), + 'stamp' => dibi::dateTime('23.1.2007 10:23'), +)); diff --git a/examples/dibi.table.php b/examples/dibi.table.php index 5752a1b4..31cf2190 100644 --- a/examples/dibi.table.php +++ b/examples/dibi.table.php @@ -1,88 +1,88 @@ -

DibiTable demo

-
- 'sqlite',
-	'database' => 'sample.sdb',
-));
-
-dibi::begin();
-
-
-// autodetection: primary keys are customer_id, order_id, ...
-DibiTable::$primaryMask = '%s_id';
-
-
-// table products
-class Products extends DibiTable
-{
-//   rely on autodetection...
-//   protected $name = 'products';
-//   protected $primary = 'product_id';
-
-}
-
-
-
-
-
-// create table object
-$products = new Products();
-
-echo "Table name: $products->name\n";
-echo "Primary key: $products->primary\n";
-
-
-// Finds rows by primary key
-foreach ($products->find(1, 3) as $row) {
-	print_r($row);
-}
-
-
-// select all
-$products->findAll()->dump();
-
-
-// select all, order by title, product_id
-$products->findAll('title', $products->primary)->dump();
-
-
-// fetches single row with id 3
-$row = $products->fetch(3);
-
-
-// deletes row from a table
-$count = $products->delete(1);
-
-// deletes multiple rows
-$count = $products->delete(array(1, 2, 3));
-var_dump($count); // number of deleted rows
-
-
-// update row #2 in a table
-$data = (object) NULL;
-$data->title = 'New title';
-$count = $products->update(2, $data);
-var_dump($count); // number of updated rows
-
-
-// update multiple rows in a table
-$count = $products->update(array(3, 5), $data);
-var_dump($count); // number of updated rows
-
-
-// inserts row into a table
-$data = array();
-$data['title'] = 'New product';
-$id = $products->insert($data);
-var_dump($id); // generated id
-
-
-// is absolutely SQL injection safe
-$key = '3 OR 1=1';
-$products->delete($key);
-// --> DELETE FROM  [products] WHERE  [product_id] IN ( 3 )
+

DibiTable demo

+
+ 'sqlite',
+	'database' => 'sample.sdb',
+));
+
+dibi::begin();
+
+
+// autodetection: primary keys are customer_id, order_id, ...
+DibiTable::$primaryMask = '%s_id';
+
+
+// table products
+class Products extends DibiTable
+{
+//   rely on autodetection...
+//   protected $name = 'products';
+//   protected $primary = 'product_id';
+
+}
+
+
+
+
+
+// create table object
+$products = new Products();
+
+echo "Table name: $products->name\n";
+echo "Primary key: $products->primary\n";
+
+
+// Finds rows by primary key
+foreach ($products->find(1, 3) as $row) {
+	print_r($row);
+}
+
+
+// select all
+$products->findAll()->dump();
+
+
+// select all, order by title, product_id
+$products->findAll('title', $products->primary)->dump();
+
+
+// fetches single row with id 3
+$row = $products->fetch(3);
+
+
+// deletes row from a table
+$count = $products->delete(1);
+
+// deletes multiple rows
+$count = $products->delete(array(1, 2, 3));
+var_dump($count); // number of deleted rows
+
+
+// update row #2 in a table
+$data = (object) NULL;
+$data->title = 'New title';
+$count = $products->update(2, $data);
+var_dump($count); // number of updated rows
+
+
+// update multiple rows in a table
+$count = $products->update(array(3, 5), $data);
+var_dump($count); // number of updated rows
+
+
+// inserts row into a table
+$data = array();
+$data['title'] = 'New product';
+$id = $products->insert($data);
+var_dump($id); // generated id
+
+
+// is absolutely SQL injection safe
+$key = '3 OR 1=1';
+$products->delete($key);
+// --> DELETE FROM  [products] WHERE  [product_id] IN ( 3 )
diff --git a/examples/dump.php b/examples/dump.php
index b3f2648e..30ed2d19 100644
--- a/examples/dump.php
+++ b/examples/dump.php
@@ -1,30 +1,30 @@
-

dibi dump example

- 'sqlite', - 'database' => 'sample.sdb', -)); - - - -$res = dibi::query(' -SELECT * FROM [products] -INNER JOIN [orders] USING ([product_id]) -INNER JOIN [customers] USING ([customer_id]) -'); - - -echo '

dibi::dump()

'; - -// dump last query (dibi::$sql) -dibi::dump(); - - -// dump result table -echo '

DibiResult::dump()

'; - -$res->dump(); +

dibi dump example

+ 'sqlite', + 'database' => 'sample.sdb', +)); + + + +$res = dibi::query(' +SELECT * FROM [products] +INNER JOIN [orders] USING ([product_id]) +INNER JOIN [customers] USING ([customer_id]) +'); + + +echo '

dibi::dump()

'; + +// dump last query (dibi::$sql) +dibi::dump(); + + +// dump result table +echo '

DibiResult::dump()

'; + +$res->dump(); diff --git a/examples/extension.method.php b/examples/extension.method.php index 964a7d0a..7612e83e 100644 --- a/examples/extension.method.php +++ b/examples/extension.method.php @@ -1,27 +1,27 @@ -

dibi extension method example

-
- 'sqlite',
-	'database' => 'sample.sdb',
-));
-
-
-
-// using the "prototype" to add custom method to class DibiResult
-function DibiResult_prototype_fetchShuffle(DibiResult $obj)
-{
-	$all = $obj->fetchAll();
-	shuffle($all);
-	return $all;
-}
-
-
-// fetch complete result set shuffled
-$res = dibi::query('SELECT * FROM [customers]');
-$all = $res->fetchShuffle();
-print_r($all);
+

dibi extension method example

+
+ 'sqlite',
+	'database' => 'sample.sdb',
+));
+
+
+
+// using the "prototype" to add custom method to class DibiResult
+function DibiResult_prototype_fetchShuffle(DibiResult $obj)
+{
+	$all = $obj->fetchAll();
+	shuffle($all);
+	return $all;
+}
+
+
+// fetch complete result set shuffled
+$res = dibi::query('SELECT * FROM [customers]');
+$all = $res->fetchShuffle();
+print_r($all);
diff --git a/examples/fetch.php b/examples/fetch.php
index ce098aba..dd7a5150 100644
--- a/examples/fetch.php
+++ b/examples/fetch.php
@@ -1,93 +1,93 @@
-

dibi fetch example

-
- 'sqlite',
-	'database' => 'sample.sdb',
-));
-
-
-/*
-TABLE products
-
-product_id | title
------------+----------
-	1      | Chair
-	2      | Table
-	3      | Computer
-
-*/
-
-
-// fetch a single row
-$row = dibi::fetch('SELECT title FROM [products]');
-print_r($row); // Chair
-echo '
'; - - -// fetch a single value -$value = dibi::fetchSingle('SELECT [title] FROM [products]'); -print_r($value); // Chair -echo '
'; - - -// fetch complete result set -$all = dibi::fetchAll('SELECT * FROM [products]'); -print_r($all); -echo '
'; - - -// fetch complete result set like association array -$res = dibi::query('SELECT * FROM [products]'); -$assoc = $res->fetchAssoc('title'); // key -print_r($assoc); -echo '
'; - - -// fetch complete result set like pairs key => value -$pairs = $res->fetchPairs('product_id', 'title'); -print_r($pairs); -echo '
'; - - -// fetch row by row -foreach ($res as $n => $row) { - print_r($row); -} -echo '
'; - - -// fetch row by row with defined offset -foreach ($res->getIterator(2) as $n => $row) { - print_r($row); -} - -// fetch row by row with defined offset and limit -foreach ($res->getIterator(2, 1) as $n => $row) { - print_r($row); -} - - -// more complex association array -$res = dibi::query(' -SELECT * -FROM [products] -INNER JOIN [orders] USING ([product_id]) -INNER JOIN [customers] USING ([customer_id]) -'); - -$assoc = $res->fetchAssoc('customers.name,products.title'); // key -print_r($assoc); -echo '
'; - -$assoc = $res->fetchAssoc('customers.name,#,products.title'); // key -print_r($assoc); -echo '
'; - -$assoc = $res->fetchAssoc('customers.name,=,products.title'); // key -print_r($assoc); -echo '
'; +

dibi fetch example

+
+ 'sqlite',
+	'database' => 'sample.sdb',
+));
+
+
+/*
+TABLE products
+
+product_id | title
+-----------+----------
+	1      | Chair
+	2      | Table
+	3      | Computer
+
+*/
+
+
+// fetch a single row
+$row = dibi::fetch('SELECT title FROM [products]');
+print_r($row); // Chair
+echo '
'; + + +// fetch a single value +$value = dibi::fetchSingle('SELECT [title] FROM [products]'); +print_r($value); // Chair +echo '
'; + + +// fetch complete result set +$all = dibi::fetchAll('SELECT * FROM [products]'); +print_r($all); +echo '
'; + + +// fetch complete result set like association array +$res = dibi::query('SELECT * FROM [products]'); +$assoc = $res->fetchAssoc('title'); // key +print_r($assoc); +echo '
'; + + +// fetch complete result set like pairs key => value +$pairs = $res->fetchPairs('product_id', 'title'); +print_r($pairs); +echo '
'; + + +// fetch row by row +foreach ($res as $n => $row) { + print_r($row); +} +echo '
'; + + +// fetch row by row with defined offset +foreach ($res->getIterator(2) as $n => $row) { + print_r($row); +} + +// fetch row by row with defined offset and limit +foreach ($res->getIterator(2, 1) as $n => $row) { + print_r($row); +} + + +// more complex association array +$res = dibi::query(' +SELECT * +FROM [products] +INNER JOIN [orders] USING ([product_id]) +INNER JOIN [customers] USING ([customer_id]) +'); + +$assoc = $res->fetchAssoc('customers.name,products.title'); // key +print_r($assoc); +echo '
'; + +$assoc = $res->fetchAssoc('customers.name,#,products.title'); // key +print_r($assoc); +echo '
'; + +$assoc = $res->fetchAssoc('customers.name,=,products.title'); // key +print_r($assoc); +echo '
'; diff --git a/examples/fluent.test.php b/examples/fluent.test.php index 0126f625..374a6363 100644 --- a/examples/fluent.test.php +++ b/examples/fluent.test.php @@ -1,63 +1,63 @@ -

dibi dump example

- 'sqlite', - 'database' => 'sample.sdb', -)); - - -$id = 10; -$record = array( - 'title' => 'Drtička na trávu', - 'price' => 318, - 'active' => TRUE, -); - -// SELECT ... -dibi::select('product_id')->as('id') - ->select('title') - ->from('products') - ->innerJoin('orders')->using('(product_id)') - ->innerJoin('customers USING (customer_id)') - ->orderBy('title') - ->test(); - -echo "\n"; - -// INSERT ... -dibi::insert('products', $record) - ->setFlag('IGNORE') - ->test(); - -echo "\n"; - -// UPDATE ... -dibi::update('products', $record) - ->where('product_id = %d', $id) - ->test(); - -echo "\n"; - -// DELETE ... -dibi::delete('products') - ->where('product_id = %d', $id) - ->test(); - -echo "\n"; - -// custom commands -dibi::command() - ->update('products') - ->where('product_id = %d', $id) - ->set($record) - ->test(); - -echo "\n"; - -dibi::command() - ->truncate('products') - ->test(); +

dibi dump example

+ 'sqlite', + 'database' => 'sample.sdb', +)); + + +$id = 10; +$record = array( + 'title' => 'Drtička na trávu', + 'price' => 318, + 'active' => TRUE, +); + +// SELECT ... +dibi::select('product_id')->as('id') + ->select('title') + ->from('products') + ->innerJoin('orders')->using('(product_id)') + ->innerJoin('customers USING (customer_id)') + ->orderBy('title') + ->test(); + +echo "\n"; + +// INSERT ... +dibi::insert('products', $record) + ->setFlag('IGNORE') + ->test(); + +echo "\n"; + +// UPDATE ... +dibi::update('products', $record) + ->where('product_id = %d', $id) + ->test(); + +echo "\n"; + +// DELETE ... +dibi::delete('products') + ->where('product_id = %d', $id) + ->test(); + +echo "\n"; + +// custom commands +dibi::command() + ->update('products') + ->where('product_id = %d', $id) + ->set($record) + ->test(); + +echo "\n"; + +dibi::command() + ->truncate('products') + ->test(); diff --git a/examples/load-sql-dump.php b/examples/load-sql-dump.php index 5eedc6b5..07071372 100644 --- a/examples/load-sql-dump.php +++ b/examples/load-sql-dump.php @@ -1,16 +1,16 @@ -

dibi import SQL dump example

-
- 'sqlite',
-	'database' => 'sample.sdb',
-));
-
-
-$count = dibi::loadFile('compress.zlib://dump.sql.gz');
-
+

dibi import SQL dump example

+
+ 'sqlite',
+	'database' => 'sample.sdb',
+));
+
+
+$count = dibi::loadFile('compress.zlib://dump.sql.gz');
+
 echo 'Number of SQL commands:', $count;
\ No newline at end of file
diff --git a/examples/logger.php b/examples/logger.php
index 0a291865..306ac821 100644
--- a/examples/logger.php
+++ b/examples/logger.php
@@ -1,32 +1,32 @@
-

dibi logger example

- 'sqlite', - 'database' => 'sample.sdb', -)); - - - -try { - $res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] = %i', 1); - - $res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < %i', 5); - - $res = dibi::query('SELECT FROM [customers] WHERE [customer_id] < %i', 38); - -} catch (DibiException $e) { - echo '

', get_class($e), ': ', $e->getMessage(), '

'; -} - - -echo "

File log.sql:

"; - -echo '
', file_get_contents('log.sql'), '
'; +

dibi logger example

+ 'sqlite', + 'database' => 'sample.sdb', +)); + + + +try { + $res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] = %i', 1); + + $res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < %i', 5); + + $res = dibi::query('SELECT FROM [customers] WHERE [customer_id] < %i', 38); + +} catch (DibiException $e) { + echo '

', get_class($e), ': ', $e->getMessage(), '

'; +} + + +echo "

File log.sql:

"; + +echo '
', file_get_contents('log.sql'), '
'; diff --git a/examples/metatypes.php b/examples/metatypes.php index d77f5b42..5e4484ff 100644 --- a/examples/metatypes.php +++ b/examples/metatypes.php @@ -1,21 +1,21 @@ -

dibi metatypes example

-
- 'sqlite',
-	'database' => 'sample.sdb',
-));
-
-
-$res = dibi::query('SELECT * FROM [customers]');
-
-// auto-convert this column to integer
-$res->setType('customer_id', Dibi::FIELD_INTEGER);
-$res->setType('added', Dibi::FIELD_DATETIME, 'H:i j.n.Y');
-
-$row = $res->fetch();
-var_dump($row);
+

dibi metatypes example

+
+ 'sqlite',
+	'database' => 'sample.sdb',
+));
+
+
+$res = dibi::query('SELECT * FROM [customers]');
+
+// auto-convert this column to integer
+$res->setType('customer_id', Dibi::FIELD_INTEGER);
+$res->setType('added', Dibi::FIELD_DATETIME, 'H:i j.n.Y');
+
+$row = $res->fetch();
+var_dump($row);
diff --git a/examples/nette-debug.php b/examples/nette-debug.php
index ae73c5c9..c26c41e7 100644
--- a/examples/nette-debug.php
+++ b/examples/nette-debug.php
@@ -1,19 +1,19 @@
-

Nette::Debug && dibi example

- 'sqlite', - 'database' => 'sample.sdb', -)); - - - -// throws error -dibi::query('SELECT FROM [customers] WHERE [customer_id] < %i', 38); +

Nette::Debug && dibi example

+ 'sqlite', + 'database' => 'sample.sdb', +)); + + + +// throws error +dibi::query('SELECT FROM [customers] WHERE [customer_id] < %i', 38); diff --git a/examples/profiler.php b/examples/profiler.php index fede2305..4e762240 100644 --- a/examples/profiler.php +++ b/examples/profiler.php @@ -1,25 +1,25 @@ -

Dibi profiler example

- 'sqlite', - 'database' => 'sample.sdb', -)); - - -for ($i=0; $i<20; $i++) { - $res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < %i', $i); -} - -?> - -

Last query:

- -

Number of queries:

- -

Elapsed time for last query: ms

- -

Total elapsed time: ms

+

Dibi profiler example

+ 'sqlite', + 'database' => 'sample.sdb', +)); + + +for ($i=0; $i<20; $i++) { + $res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < %i', $i); +} + +?> + +

Last query:

+ +

Number of queries:

+ +

Elapsed time for last query: ms

+ +

Total elapsed time: ms

diff --git a/examples/sql-builder.php b/examples/sql-builder.php index e9fcb777..256c8dcf 100644 --- a/examples/sql-builder.php +++ b/examples/sql-builder.php @@ -1,66 +1,66 @@ - -

dibi SQL builder example

-
- 'sqlite',
-	'database' => 'sample.sdb',
-));
-
-
-// dibi detects INSERT or REPLACE command
-dibi::test('
-REPLACE INTO [products]', array(
-	'title'  => 'Drtika na trvu',
-	'price'  => 318,
-	'active' => TRUE,
-));
-
-
-// multiple INSERT command
-$array = array(
-	'title'   => 'Super Product',
-	'price'   => 12,
-	'brand'   => NULL,
-	'created' => dibi::datetime(),
-);
-dibi::test("INSERT INTO [products]", $array, $array, $array);
-
-
-// dibi detects UPDATE command
-dibi::test("
-UPDATE [colors] SET", array(
-	'color' => 'blue',
-	'order' => 12,
-), "
-WHERE [id]=%i", 123);
-
-
-// SELECT
-$ipMask = '192.168.%';
-$timestamp = mktime(0, 0, 0, 10, 13, 1997);
-
-dibi::test('
-SELECT COUNT(*) as [count]
-FROM [comments]
-WHERE [ip] LIKE %s', $ipMask, '
-AND [date] > ', dibi::date($timestamp)
-);
-
-
-// IN array
-$array = array(1, 2, 3);
-dibi::test("
-SELECT *
-FROM [people]
-WHERE [id] IN (", $array, ")
-");
+
+

dibi SQL builder example

+
+ 'sqlite',
+	'database' => 'sample.sdb',
+));
+
+
+// dibi detects INSERT or REPLACE command
+dibi::test('
+REPLACE INTO [products]', array(
+	'title'  => 'Drtika na trvu',
+	'price'  => 318,
+	'active' => TRUE,
+));
+
+
+// multiple INSERT command
+$array = array(
+	'title'   => 'Super Product',
+	'price'   => 12,
+	'brand'   => NULL,
+	'created' => dibi::datetime(),
+);
+dibi::test("INSERT INTO [products]", $array, $array, $array);
+
+
+// dibi detects UPDATE command
+dibi::test("
+UPDATE [colors] SET", array(
+	'color' => 'blue',
+	'order' => 12,
+), "
+WHERE [id]=%i", 123);
+
+
+// SELECT
+$ipMask = '192.168.%';
+$timestamp = mktime(0, 0, 0, 10, 13, 1997);
+
+dibi::test('
+SELECT COUNT(*) as [count]
+FROM [comments]
+WHERE [ip] LIKE %s', $ipMask, '
+AND [date] > ', dibi::date($timestamp)
+);
+
+
+// IN array
+$array = array(1, 2, 3);
+dibi::test("
+SELECT *
+FROM [people]
+WHERE [id] IN (", $array, ")
+");
diff --git a/examples/sql-condition.php b/examples/sql-condition.php
index 4c373c8a..f17cf123 100644
--- a/examples/sql-condition.php
+++ b/examples/sql-condition.php
@@ -1,51 +1,51 @@
-
-

dibi conditional SQL example

-
- 'sqlite',
-	'database' => 'sample.sdb',
-));
-
-
-$cond1 = rand(0,2) < 1;
-$cond2 = rand(0,2) < 1;
-$foo = -1;
-$bar = 2;
-
-
-$name = $cond1 ? 'K%' : NULL;
-
-// if & end
-dibi::test('
-SELECT *
-FROM [customers]
-%if', isset($name), 'WHERE [name] LIKE %s', $name, '%end'
-);
-
-
-// if & else & (optional) end
-dibi::test("
-SELECT *
-FROM [people]
-WHERE [id] > 0
-	%if", ($foo > 0), "AND [foo]=%i", $foo, "
-	%else %if", ($bar > 0), "AND [bar]=%i", $bar, "
-");
-
-
-// nested condition
-dibi::test('
-SELECT *
-FROM [customers]
-WHERE
-	%if', isset($name), '[name] LIKE %s', $name, '
-		%if', $cond2, 'AND [admin]=1 %end
-	%else 1 LIMIT 10 %end'
-);
+
+

dibi conditional SQL example

+
+ 'sqlite',
+	'database' => 'sample.sdb',
+));
+
+
+$cond1 = rand(0,2) < 1;
+$cond2 = rand(0,2) < 1;
+$foo = -1;
+$bar = 2;
+
+
+$name = $cond1 ? 'K%' : NULL;
+
+// if & end
+dibi::test('
+SELECT *
+FROM [customers]
+%if', isset($name), 'WHERE [name] LIKE %s', $name, '%end'
+);
+
+
+// if & else & (optional) end
+dibi::test("
+SELECT *
+FROM [people]
+WHERE [id] > 0
+	%if", ($foo > 0), "AND [foo]=%i", $foo, "
+	%else %if", ($bar > 0), "AND [bar]=%i", $bar, "
+");
+
+
+// nested condition
+dibi::test('
+SELECT *
+FROM [customers]
+WHERE
+	%if', isset($name), '[name] LIKE %s', $name, '
+		%if', $cond2, 'AND [admin]=1 %end
+	%else 1 LIMIT 10 %end'
+);
diff --git a/examples/table-prefix.php b/examples/table-prefix.php
index 9a36ceae..fc13185c 100644
--- a/examples/table-prefix.php
+++ b/examples/table-prefix.php
@@ -1,44 +1,44 @@
-

dibi prefix & substitute example

- 'sqlite', - 'database' => 'sample.sdb', -)); - - -// create new substitution :blog: ==> wp_ -dibi::addSubst('blog', 'wp_'); - - -// generate and dump SQL -dibi::test("UPDATE [:blog:items] SET [text]='Hello World'"); - - - - -// create new substitution :: (empty) ==> my_ -dibi::addSubst('', 'my_'); - - -// generate and dump SQL -dibi::test("UPDATE [database.::table] SET [text]='Hello World'"); - - - - - -function substFallBack($expr) -{ - return 'the_' . $expr; -} - -// create substitution fallback -dibi::setSubstFallBack('substFallBack'); - - -// generate and dump SQL -dibi::test("UPDATE [:account:user] SET [name]='John Doe'"); +

dibi prefix & substitute example

+ 'sqlite', + 'database' => 'sample.sdb', +)); + + +// create new substitution :blog: ==> wp_ +dibi::addSubst('blog', 'wp_'); + + +// generate and dump SQL +dibi::test("UPDATE [:blog:items] SET [text]='Hello World'"); + + + + +// create new substitution :: (empty) ==> my_ +dibi::addSubst('', 'my_'); + + +// generate and dump SQL +dibi::test("UPDATE [database.::table] SET [text]='Hello World'"); + + + + + +function substFallBack($expr) +{ + return 'the_' . $expr; +} + +// create substitution fallback +dibi::setSubstFallBack('substFallBack'); + + +// generate and dump SQL +dibi::test("UPDATE [:account:user] SET [name]='John Doe'"); diff --git a/examples/transaction.php b/examples/transaction.php index 7b316e8b..14f43443 100644 --- a/examples/transaction.php +++ b/examples/transaction.php @@ -1,28 +1,28 @@ -

dibi transaction example

-
- 'sqlite',
-	'database' => 'sample.sdb',
-));
-
-
-echo "

Before:

\n"; -dibi::query('SELECT * FROM [products]')->dump(); - - - -dibi::begin(); -dibi::query('INSERT INTO [products]', array( - 'title' => 'Test product', -)); -dibi::rollback(); // or dibi::commit(); - - - -echo "

After:

\n"; +

dibi transaction example

+
+ 'sqlite',
+	'database' => 'sample.sdb',
+));
+
+
+echo "

Before:

\n"; +dibi::query('SELECT * FROM [products]')->dump(); + + + +dibi::begin(); +dibi::query('INSERT INTO [products]', array( + 'title' => 'Test product', +)); +dibi::rollback(); // or dibi::commit(); + + + +echo "

After:

\n"; dibi::query('SELECT * FROM [products]')->dump(); \ No newline at end of file diff --git a/icons/example.html b/icons/example.html index 80cfd835..f6e0e7f2 100644 --- a/icons/example.html +++ b/icons/example.html @@ -1,2 +1,2 @@ -dibi powered +dibi powered diff --git a/license.cs.txt b/license.cs.txt index 1c73580d..9f7bb367 100644 --- a/license.cs.txt +++ b/license.cs.txt @@ -1,69 +1,69 @@ ----------------------------------------------------------------------------------- -Tento text je NEOFICILNM pekladem "Dibi license". Nevyjaduje prvn podstatu -podmnek pro en tohoto softwaru - k tomuto elu slou vhradn pvodn -anglick verze licence. ----------------------------------------------------------------------------------- - - -Dibi Licence, verze 1 -====================== - -Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com) -Vechna prva vyhrazena. - -Tato licence je prvn ujednn mezi vmi a Davidem Grudlem (dle Author) -pro poteby pouit "dibi" (dle "Software"). Zsknm, pouitm -a/nebo zkoprovnm Software projevujete souhlas s tm, e jste peetli, -porozumli a budete jednat v souladu s podmnkami tto licence. - - -POVOLEN POUIT ----------------- - -Je povoleno pouvat, koprovat, modifikovat a distribuovat Software -a jeho dokumentaci, v pvodnm i upravovanm tvaru, pro jakkoliv el, -za pedpokladu, e jsou splnny tyto podmnky: - -1. Kopie tto licenn smlouvy mus bt soust distribuce. - -2. en zdrojov kd mus zachovat ve uvedenou informaci o autorskch - prvech ve vech souborech zdrojovho kdu. - -3. en binrn tvar mus reprodukovat ve uvedenou informaci o autorskch - prvech v dokumentaci a/nebo jinch materilech poskytovanch s distribuc. - -4. Produkty odvozen od Software mus obsahovat potvrzen, e jsou odvozen - od "dibi", ve sv dokumentaci a/nebo jinch materilech - poskytovanch s distribuc. - -5. Nzev "dibi" nesm bt pouit pi podpoe nebo propagaci produkt - odvozenmi ze Software bez pedchozho psemnho souhlasu Autora. - -6. Produkty odvozen od Software nesm bt nazvny "dibi", - ani se nesm "dibi" objevit v jejich nzvu bez pedchozho - psemnho souhlasu Autora. - - -ZBAVEN ZODPOVDNOSTI ---------------------- - -Souhlaste se zbavenm zodpovdnosti a krytm Autora a pispvatel vi -jakmkoliv pmm, nepmm, nhodnm nebo nslednm odjinud pochzejcm kodm, -alobm nebo sporm, jako i ped vemi souvisejcmi nklady, zvazky, -odkodnnmi, hradami nebo poplatky vyplvajcch z pouvn nebo -nesprvnho uit Software, nebo z poruen podmnek tto licence. - - -ZRUKA SE NEPOSKYTUJE ---------------------- - -TENTO SOFTWARE JE POSKYTOVN DRITELEM LICENCE A JEHO PISPVATELI "JAK STOJ A LE" -A JAKKOLIV VSLOVN NEBO PEDPOKLDAN ZRUKY VETN, ALE NEJEN, PEDPOKLDANCH -OBCHODNCH ZRUK A ZRUKY VHODNOSTI PRO JAKKOLIV EL JSOU POPENY. -DRITEL, ANI PISPVATEL NEBUDOU V DNM PPAD ODPOVDNI ZA JAKKOLIV PM, -NEPM, NHODN, ZVLTN, PKLADN NEBO VYPLVAJC KODY (VETN, ALE NEJEN, -KOD VZNIKLCH NARUENM DODVEK ZBO NEBO SLUEB; ZTRTOU POUITELNOSTI, -DAT NEBO ZISK; NEBO PERUENM OBCHODN INNOSTI) JAKKOLIV ZPSOBEN NA ZKLAD -JAKKOLIV TEORIE O ZODPOVDNOSTI, A U PLYNOUC Z JINHO SMLUVNHO VZTAHU, -URIT ZODPOVDNOSTI NEBO PEINU (VETN NEDBALOSTI) NA JAKMKOLIV ZPSOBU POUIT +---------------------------------------------------------------------------------- +Tento text je NEOFICILNM pekladem "Dibi license". Nevyjaduje prvn podstatu +podmnek pro en tohoto softwaru - k tomuto elu slou vhradn pvodn +anglick verze licence. +---------------------------------------------------------------------------------- + + +Dibi Licence, verze 1 +====================== + +Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com) +Vechna prva vyhrazena. + +Tato licence je prvn ujednn mezi vmi a Davidem Grudlem (dle Author) +pro poteby pouit "dibi" (dle "Software"). Zsknm, pouitm +a/nebo zkoprovnm Software projevujete souhlas s tm, e jste peetli, +porozumli a budete jednat v souladu s podmnkami tto licence. + + +POVOLEN POUIT +---------------- + +Je povoleno pouvat, koprovat, modifikovat a distribuovat Software +a jeho dokumentaci, v pvodnm i upravovanm tvaru, pro jakkoliv el, +za pedpokladu, e jsou splnny tyto podmnky: + +1. Kopie tto licenn smlouvy mus bt soust distribuce. + +2. en zdrojov kd mus zachovat ve uvedenou informaci o autorskch + prvech ve vech souborech zdrojovho kdu. + +3. en binrn tvar mus reprodukovat ve uvedenou informaci o autorskch + prvech v dokumentaci a/nebo jinch materilech poskytovanch s distribuc. + +4. Produkty odvozen od Software mus obsahovat potvrzen, e jsou odvozen + od "dibi", ve sv dokumentaci a/nebo jinch materilech + poskytovanch s distribuc. + +5. Nzev "dibi" nesm bt pouit pi podpoe nebo propagaci produkt + odvozenmi ze Software bez pedchozho psemnho souhlasu Autora. + +6. Produkty odvozen od Software nesm bt nazvny "dibi", + ani se nesm "dibi" objevit v jejich nzvu bez pedchozho + psemnho souhlasu Autora. + + +ZBAVEN ZODPOVDNOSTI +--------------------- + +Souhlaste se zbavenm zodpovdnosti a krytm Autora a pispvatel vi +jakmkoliv pmm, nepmm, nhodnm nebo nslednm odjinud pochzejcm kodm, +alobm nebo sporm, jako i ped vemi souvisejcmi nklady, zvazky, +odkodnnmi, hradami nebo poplatky vyplvajcch z pouvn nebo +nesprvnho uit Software, nebo z poruen podmnek tto licence. + + +ZRUKA SE NEPOSKYTUJE +--------------------- + +TENTO SOFTWARE JE POSKYTOVN DRITELEM LICENCE A JEHO PISPVATELI "JAK STOJ A LE" +A JAKKOLIV VSLOVN NEBO PEDPOKLDAN ZRUKY VETN, ALE NEJEN, PEDPOKLDANCH +OBCHODNCH ZRUK A ZRUKY VHODNOSTI PRO JAKKOLIV EL JSOU POPENY. +DRITEL, ANI PISPVATEL NEBUDOU V DNM PPAD ODPOVDNI ZA JAKKOLIV PM, +NEPM, NHODN, ZVLTN, PKLADN NEBO VYPLVAJC KODY (VETN, ALE NEJEN, +KOD VZNIKLCH NARUENM DODVEK ZBO NEBO SLUEB; ZTRTOU POUITELNOSTI, +DAT NEBO ZISK; NEBO PERUENM OBCHODN INNOSTI) JAKKOLIV ZPSOBEN NA ZKLAD +JAKKOLIV TEORIE O ZODPOVDNOSTI, A U PLYNOUC Z JINHO SMLUVNHO VZTAHU, +URIT ZODPOVDNOSTI NEBO PEINU (VETN NEDBALOSTI) NA JAKMKOLIV ZPSOBU POUIT TOHOTO SOFTWARE, I V PPAD, E DRITEL PRV BYL UPOZORNN NA MONOST TAKOVCH KOD. \ No newline at end of file diff --git a/license.txt b/license.txt index 5d95148f..40e698dd 100644 --- a/license.txt +++ b/license.txt @@ -1,62 +1,62 @@ -The Dibi License, Version 1 -============================ - -Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com) -All rights reserved. - -This license is a legal agreement between you and David Grudl (the "Author") -for the use of "dibi" (the "Software"). By obtaining, using and/or -copying the Software, you agree that you have read, understood, and will -comply with the terms and conditions of this license. - - -PERMITTED USE -------------- - -You are permitted to use, copy, modify, and distribute the Software and its -documentation, with or without modification, for any purpose, provided that -the following conditions are met: - -1. A copy of this license agreement must be included with the distribution. - -2. Redistributions of source code must retain the above copyright notice in - all source code files. - -3. Redistributions in binary form must reproduce the above copyright notice - in the documentation and/or other materials provided with the distribution. - -4. Products derived from the Software must include an acknowledgment that - they are derived from "dibi" in their documentation and/or other - materials provided with the distribution. - -5. The name "dibi" must not be used to endorse or promote products - derived from the Software without prior written permission from Author. - -6. Products derived from the Software may not be called "dibi", - nor may "dibi" appear in their name, without prior written - permission from Author. - - -INDEMNITY ---------- - -You agree to indemnify and hold harmless the Author and any contributors -for any direct, indirect, incidental, or consequential third-party claims, -actions or suits, as well as any related expenses, liabilities, damages, -settlements or fees arising from your use or misuse of the Software, -or a violation of any terms of this license. - - -DISCLAIMER OF WARRANTY ----------------------- - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +The Dibi License, Version 1 +============================ + +Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com) +All rights reserved. + +This license is a legal agreement between you and David Grudl (the "Author") +for the use of "dibi" (the "Software"). By obtaining, using and/or +copying the Software, you agree that you have read, understood, and will +comply with the terms and conditions of this license. + + +PERMITTED USE +------------- + +You are permitted to use, copy, modify, and distribute the Software and its +documentation, with or without modification, for any purpose, provided that +the following conditions are met: + +1. A copy of this license agreement must be included with the distribution. + +2. Redistributions of source code must retain the above copyright notice in + all source code files. + +3. Redistributions in binary form must reproduce the above copyright notice + in the documentation and/or other materials provided with the distribution. + +4. Products derived from the Software must include an acknowledgment that + they are derived from "dibi" in their documentation and/or other + materials provided with the distribution. + +5. The name "dibi" must not be used to endorse or promote products + derived from the Software without prior written permission from Author. + +6. Products derived from the Software may not be called "dibi", + nor may "dibi" appear in their name, without prior written + permission from Author. + + +INDEMNITY +--------- + +You agree to indemnify and hold harmless the Author and any contributors +for any direct, indirect, incidental, or consequential third-party claims, +actions or suits, as well as any related expenses, liabilities, damages, +settlements or fees arising from your use or misuse of the Software, +or a violation of any terms of this license. + + +DISCLAIMER OF WARRANTY +---------------------- + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/readme.txt b/readme.txt index d394c60f..8c55f45d 100644 --- a/readme.txt +++ b/readme.txt @@ -1,41 +1,41 @@ -Dibi (c) David Grudl, 2005-2008 (http://davidgrudl.com) - - - -Introduction ------------- - -Thank you for downloading Dibi! - -Database access functions in PHP are not standardised. This is class library -to hide the differences between the different databases access. - -The files in this archive are released under the Dibi license. -See license.txt in this directory for a copy of the license. - - - -Documentation and Examples --------------------------- - -Refer to the 'examples' directory for examples. Dibi documentation is -available on the homepage: - -http://dibiphp.com - - - -Dibi.compact ------------- - -This is shrinked single-file version of whole Dibi, useful when you don't -want to modify library, but just use it. - -This is exactly the same as normal version, just only comments and -whitespaces are removed. - - - ------ -For more information, visit the author's weblog (in czech language): -http://phpfashion.com +Dibi (c) David Grudl, 2005-2008 (http://davidgrudl.com) + + + +Introduction +------------ + +Thank you for downloading Dibi! + +Database access functions in PHP are not standardised. This is class library +to hide the differences between the different databases access. + +The files in this archive are released under the Dibi license. +See license.txt in this directory for a copy of the license. + + + +Documentation and Examples +-------------------------- + +Refer to the 'examples' directory for examples. Dibi documentation is +available on the homepage: + +http://dibiphp.com + + + +Dibi.compact +------------ + +This is shrinked single-file version of whole Dibi, useful when you don't +want to modify library, but just use it. + +This is exactly the same as normal version, just only comments and +whitespaces are removed. + + + +----- +For more information, visit the author's weblog (in czech language): +http://phpfashion.com diff --git a/version.txt b/version.txt index a89c6c34..a111776a 100644 --- a/version.txt +++ b/version.txt @@ -1,4 +1,3 @@ -Dibi version 0.9 - -Revision: $WCREV$ -Date: $WCDATE$ +Dibi 0.9 (revision $WCREV$) + +Released on $WCDATE$