diff --git a/thirdparty/pear/DB.php b/thirdparty/pear/DB.php new file mode 100644 index 0000000..69477cb --- /dev/null +++ b/thirdparty/pear/DB.php @@ -0,0 +1,1114 @@ + | +// | Tomas V.V.Cox | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ +// +// Database independent query interface. + + +require_once 'PEAR.php'; + +// {{{ constants +// {{{ error codes + +/* + * The method mapErrorCode in each DB_dbtype implementation maps + * native error codes to one of these. + * + * If you add an error code here, make sure you also add a textual + * version of it in DB::errorMessage(). + */ +define('DB_OK', 1); +define('DB_ERROR', -1); +define('DB_ERROR_SYNTAX', -2); +define('DB_ERROR_CONSTRAINT', -3); +define('DB_ERROR_NOT_FOUND', -4); +define('DB_ERROR_ALREADY_EXISTS', -5); +define('DB_ERROR_UNSUPPORTED', -6); +define('DB_ERROR_MISMATCH', -7); +define('DB_ERROR_INVALID', -8); +define('DB_ERROR_NOT_CAPABLE', -9); +define('DB_ERROR_TRUNCATED', -10); +define('DB_ERROR_INVALID_NUMBER', -11); +define('DB_ERROR_INVALID_DATE', -12); +define('DB_ERROR_DIVZERO', -13); +define('DB_ERROR_NODBSELECTED', -14); +define('DB_ERROR_CANNOT_CREATE', -15); +define('DB_ERROR_CANNOT_DELETE', -16); +define('DB_ERROR_CANNOT_DROP', -17); +define('DB_ERROR_NOSUCHTABLE', -18); +define('DB_ERROR_NOSUCHFIELD', -19); +define('DB_ERROR_NEED_MORE_DATA', -20); +define('DB_ERROR_NOT_LOCKED', -21); +define('DB_ERROR_VALUE_COUNT_ON_ROW', -22); +define('DB_ERROR_INVALID_DSN', -23); +define('DB_ERROR_CONNECT_FAILED', -24); +define('DB_ERROR_EXTENSION_NOT_FOUND',-25); +define('DB_ERROR_ACCESS_VIOLATION', -26); +define('DB_ERROR_NOSUCHDB', -27); +define('DB_ERROR_CONSTRAINT_NOT_NULL',-29); + + +// }}} +// {{{ prepared statement-related + + +/* + * These constants are used when storing information about prepared + * statements (using the "prepare" method in DB_dbtype). + * + * The prepare/execute model in DB is mostly borrowed from the ODBC + * extension, in a query the "?" character means a scalar parameter. + * There are two extensions though, a "&" character means an opaque + * parameter. An opaque parameter is simply a file name, the real + * data are in that file (useful for putting uploaded files into your + * database and such). The "!" char means a parameter that must be + * left as it is. + * They modify the quote behavoir: + * DB_PARAM_SCALAR (?) => 'original string quoted' + * DB_PARAM_OPAQUE (&) => 'string from file quoted' + * DB_PARAM_MISC (!) => original string + */ +define('DB_PARAM_SCALAR', 1); +define('DB_PARAM_OPAQUE', 2); +define('DB_PARAM_MISC', 3); + + +// }}} +// {{{ binary data-related + + +/* + * These constants define different ways of returning binary data + * from queries. Again, this model has been borrowed from the ODBC + * extension. + * + * DB_BINMODE_PASSTHRU sends the data directly through to the browser + * when data is fetched from the database. + * DB_BINMODE_RETURN lets you return data as usual. + * DB_BINMODE_CONVERT returns data as well, only it is converted to + * hex format, for example the string "123" would become "313233". + */ +define('DB_BINMODE_PASSTHRU', 1); +define('DB_BINMODE_RETURN', 2); +define('DB_BINMODE_CONVERT', 3); + + +// }}} +// {{{ fetch modes + + +/** + * This is a special constant that tells DB the user hasn't specified + * any particular get mode, so the default should be used. + */ +define('DB_FETCHMODE_DEFAULT', 0); + +/** + * Column data indexed by numbers, ordered from 0 and up + */ +define('DB_FETCHMODE_ORDERED', 1); + +/** + * Column data indexed by column names + */ +define('DB_FETCHMODE_ASSOC', 2); + +/** + * Column data as object properties + */ +define('DB_FETCHMODE_OBJECT', 3); + +/** + * For multi-dimensional results: normally the first level of arrays + * is the row number, and the second level indexed by column number or name. + * DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays + * is the column name, and the second level the row number. + */ +define('DB_FETCHMODE_FLIPPED', 4); + +/* for compatibility */ +define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED); +define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC); +define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED); + + +// }}} +// {{{ tableInfo() && autoPrepare()-related + + +/** + * these are constants for the tableInfo-function + * they are bitwised or'ed. so if there are more constants to be defined + * in the future, adjust DB_TABLEINFO_FULL accordingly + */ +define('DB_TABLEINFO_ORDER', 1); +define('DB_TABLEINFO_ORDERTABLE', 2); +define('DB_TABLEINFO_FULL', 3); + +/* + * Used by autoPrepare() + */ +define('DB_AUTOQUERY_INSERT', 1); +define('DB_AUTOQUERY_UPDATE', 2); + + +// }}} +// {{{ portability modes + + +/** + * Portability: turn off all portability features. + * @see DB_common::setOption() + */ +define('DB_PORTABILITY_NONE', 0); + +/** + * Portability: convert names of tables and fields to lower case + * when using the get*(), fetch*() and tableInfo() methods. + * @see DB_common::setOption() + */ +define('DB_PORTABILITY_LOWERCASE', 1); + +/** + * Portability: right trim the data output by get*() and fetch*(). + * @see DB_common::setOption() + */ +define('DB_PORTABILITY_RTRIM', 2); + +/** + * Portability: force reporting the number of rows deleted. + * @see DB_common::setOption() + */ +define('DB_PORTABILITY_DELETE_COUNT', 4); + +/** + * Portability: enable hack that makes numRows() work in Oracle. + * @see DB_common::setOption() + */ +define('DB_PORTABILITY_NUMROWS', 8); + +/** + * Portability: makes certain error messages in certain drivers compatible + * with those from other DBMS's. + * + * + mysql, mysqli: change unique/primary key constraints + * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT + * + * + odbc(access): MS's ODBC driver reports 'no such field' as code + * 07001, which means 'too few parameters.' When this option is on + * that code gets mapped to DB_ERROR_NOSUCHFIELD. + * + * @see DB_common::setOption() + */ +define('DB_PORTABILITY_ERRORS', 16); + +/** + * Portability: convert null values to empty strings in data output by + * get*() and fetch*(). + * @see DB_common::setOption() + */ +define('DB_PORTABILITY_NULL_TO_EMPTY', 32); + +/** + * Portability: turn on all portability features. + * @see DB_common::setOption() + */ +define('DB_PORTABILITY_ALL', 63); + +// }}} + + +// }}} +// {{{ class DB + +/** + * The main "DB" class is simply a container class with some static + * methods for creating DB objects as well as some utility functions + * common to all parts of DB. + * + * The object model of DB is as follows (indentation means inheritance): + * + * DB The main DB class. This is simply a utility class + * with some "static" methods for creating DB objects as + * well as common utility functions for other DB classes. + * + * DB_common The base for each DB implementation. Provides default + * | implementations (in OO lingo virtual methods) for + * | the actual DB implementations as well as a bunch of + * | query utility functions. + * | + * +-DB_mysql The DB implementation for MySQL. Inherits DB_common. + * When calling DB::factory or DB::connect for MySQL + * connections, the object returned is an instance of this + * class. + * + * @package DB + * @author Stig Bakken + * @author Tomas V.V.Cox + * @since PHP 4.0 + * @version $Id$ + * @category Database + */ +class DB +{ + // {{{ &factory() + + /** + * Create a new DB object for the specified database type. + * + * Allows creation of a DB_ object from which the object's + * methods can be utilized without actually connecting to a database. + * + * @param string $type database type, for example "mysql" + * @param array $options associative array of option names and values + * + * @return object a new DB object. On error, an error object. + * + * @see DB_common::setOption() + * @access public + */ + function &factory($type, $options = false) + { + if (!is_array($options)) { + $options = array('persistent' => $options); + } + + if (isset($options['debug']) && $options['debug'] >= 2) { + // expose php errors with sufficient debug level + include_once "DB/{$type}.php"; + } else { + @include_once "DB/{$type}.php"; + } + + $classname = "DB_${type}"; + + if (!class_exists($classname)) { + $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, + "Unable to include the DB/{$type}.php file", + 'DB_Error', true); + return $tmp; + } + + @$obj =& new $classname; + + foreach ($options as $option => $value) { + $test = $obj->setOption($option, $value); + if (DB::isError($test)) { + return $test; + } + } + + return $obj; + } + + // }}} + // {{{ &connect() + + /** + * Create a new DB object and connect to the specified database. + * + * Example 1. + * 2, + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $dbh =& DB::connect($dsn, $options); + * if (DB::isError($dbh)) { + * die($dbh->getMessage()); + * } + * ?> + * + * @param mixed $dsn string "data source name" or an array in the + * format returned by DB::parseDSN() + * + * @param array $options an associative array of option names and + * their values + * + * @return object a newly created DB connection object, or a DB + * error object on error + * + * @see DB::parseDSN(), DB_common::setOption(), DB::isError() + * @access public + */ + function &connect($dsn, $options = array()) + { + $dsninfo = DB::parseDSN($dsn); + $type = $dsninfo['phptype']; + + if (!is_array($options)) { + /* + * For backwards compatibility. $options used to be boolean, + * indicating whether the connection should be persistent. + */ + $options = array('persistent' => $options); + } + + if (isset($options['debug']) && $options['debug'] >= 2) { + // expose php errors with sufficient debug level + include_once "DB/${type}.php"; + } else { + @include_once "DB/${type}.php"; + } + + $classname = "DB_${type}"; + if (!class_exists($classname)) { + $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, + "Unable to include the DB/{$type}.php file for `$dsn'", + 'DB_Error', true); + return $tmp; + } + + @$obj =& new $classname; + + foreach ($options as $option => $value) { + $test = $obj->setOption($option, $value); + if (DB::isError($test)) { + return $test; + } + } + + $err = $obj->connect($dsninfo, $obj->getOption('persistent')); + if (DB::isError($err)) { + $err->addUserInfo($dsn); + return $err; + } + + return $obj; + } + + // }}} + // {{{ apiVersion() + + /** + * Return the DB API version + * + * @return int the DB API version number + * + * @access public + */ + function apiVersion() + { + return 2; + } + + // }}} + // {{{ isError() + + /** + * Tell whether a result code from a DB method is an error + * + * @param int $value result code + * + * @return bool whether $value is an error + * + * @access public + */ + function isError($value) + { + return is_a($value, 'DB_Error'); + } + + // }}} + // {{{ isConnection() + + /** + * Tell whether a value is a DB connection + * + * @param mixed $value value to test + * + * @return bool whether $value is a DB connection + * + * @access public + */ + function isConnection($value) + { + return (is_object($value) && + is_subclass_of($value, 'db_common') && + method_exists($value, 'simpleQuery')); + } + + // }}} + // {{{ isManip() + + /** + * Tell whether a query is a data manipulation query (insert, + * update or delete) or a data definition query (create, drop, + * alter, grant, revoke). + * + * @access public + * + * @param string $query the query + * + * @return boolean whether $query is a data manipulation query + */ + function isManip($query) + { + $manips = 'INSERT|UPDATE|DELETE|LOAD DATA|'.'REPLACE|CREATE|DROP|'. + 'ALTER|GRANT|REVOKE|'.'LOCK|UNLOCK'; + if (preg_match('/^\s*"?('.$manips.')\s+/i', $query)) { + return true; + } + return false; + } + + // }}} + // {{{ errorMessage() + + /** + * Return a textual error message for a DB error code + * + * @param integer $value error code + * + * @return string error message, or false if the error code was + * not recognized + */ + function errorMessage($value) + { + static $errorMessages; + if (!isset($errorMessages)) { + $errorMessages = array( + DB_ERROR => 'unknown error', + DB_ERROR_ALREADY_EXISTS => 'already exists', + DB_ERROR_CANNOT_CREATE => 'can not create', + DB_ERROR_CANNOT_DELETE => 'can not delete', + DB_ERROR_CANNOT_DROP => 'can not drop', + DB_ERROR_CONSTRAINT => 'constraint violation', + DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', + DB_ERROR_DIVZERO => 'division by zero', + DB_ERROR_INVALID => 'invalid', + DB_ERROR_INVALID_DATE => 'invalid date or time', + DB_ERROR_INVALID_NUMBER => 'invalid number', + DB_ERROR_MISMATCH => 'mismatch', + DB_ERROR_NODBSELECTED => 'no database selected', + DB_ERROR_NOSUCHFIELD => 'no such field', + DB_ERROR_NOSUCHTABLE => 'no such table', + DB_ERROR_NOT_CAPABLE => 'DB backend not capable', + DB_ERROR_NOT_FOUND => 'not found', + DB_ERROR_NOT_LOCKED => 'not locked', + DB_ERROR_SYNTAX => 'syntax error', + DB_ERROR_UNSUPPORTED => 'not supported', + DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', + DB_ERROR_INVALID_DSN => 'invalid DSN', + DB_ERROR_CONNECT_FAILED => 'connect failed', + DB_OK => 'no error', + DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', + DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', + DB_ERROR_NOSUCHDB => 'no such database', + DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions', + DB_ERROR_TRUNCATED => 'truncated' + ); + } + + if (DB::isError($value)) { + $value = $value->getCode(); + } + + return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[DB_ERROR]; + } + + // }}} + // {{{ parseDSN() + + /** + * Parse a data source name. + * + * Additional keys can be added by appending a URI query string to the + * end of the DSN. + * + * The format of the supplied DSN is in its fullest form: + * + * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true + * + * + * Most variations are allowed: + * + * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644 + * phptype://username:password@hostspec/database_name + * phptype://username:password@hostspec + * phptype://username@hostspec + * phptype://hostspec/database + * phptype://hostspec + * phptype(dbsyntax) + * phptype + * + * + * @param string $dsn Data Source Name to be parsed + * + * @return array an associative array with the following keys: + * + phptype: Database backend used in PHP (mysql, odbc etc.) + * + dbsyntax: Database used with regards to SQL syntax etc. + * + protocol: Communication protocol to use (tcp, unix etc.) + * + hostspec: Host specification (hostname[:port]) + * + database: Database to use on the DBMS server + * + username: User name for login + * + password: Password for login + * + * @author Tomas V.V.Cox + */ + function parseDSN($dsn) + { + $parsed = array( + 'phptype' => false, + 'dbsyntax' => false, + 'username' => false, + 'password' => false, + 'protocol' => false, + 'hostspec' => false, + 'port' => false, + 'socket' => false, + 'database' => false, + ); + + if (is_array($dsn)) { + $dsn = array_merge($parsed, $dsn); + if (!$dsn['dbsyntax']) { + $dsn['dbsyntax'] = $dsn['phptype']; + } + return $dsn; + } + + // Find phptype and dbsyntax + if (($pos = strpos($dsn, '://')) !== false) { + $str = substr($dsn, 0, $pos); + $dsn = substr($dsn, $pos + 3); + } else { + $str = $dsn; + $dsn = null; + } + + // Get phptype and dbsyntax + // $str => phptype(dbsyntax) + if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { + $parsed['phptype'] = $arr[1]; + $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2]; + } else { + $parsed['phptype'] = $str; + $parsed['dbsyntax'] = $str; + } + + if (!count($dsn)) { + return $parsed; + } + + // Get (if found): username and password + // $dsn => username:password@protocol+hostspec/database + if (($at = strrpos($dsn,'@')) !== false) { + $str = substr($dsn, 0, $at); + $dsn = substr($dsn, $at + 1); + if (($pos = strpos($str, ':')) !== false) { + $parsed['username'] = rawurldecode(substr($str, 0, $pos)); + $parsed['password'] = rawurldecode(substr($str, $pos + 1)); + } else { + $parsed['username'] = rawurldecode($str); + } + } + + // Find protocol and hostspec + + // $dsn => proto(proto_opts)/database + if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { + $proto = $match[1]; + $proto_opts = $match[2] ? $match[2] : false; + $dsn = $match[3]; + + // $dsn => protocol+hostspec/database (old format) + } else { + if (strpos($dsn, '+') !== false) { + list($proto, $dsn) = explode('+', $dsn, 2); + } + if (strpos($dsn, '/') !== false) { + list($proto_opts, $dsn) = explode('/', $dsn, 2); + } else { + $proto_opts = $dsn; + $dsn = null; + } + } + + // process the different protocol options + $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; + $proto_opts = rawurldecode($proto_opts); + if ($parsed['protocol'] == 'tcp') { + if (strpos($proto_opts, ':') !== false) { + list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts); + } else { + $parsed['hostspec'] = $proto_opts; + } + } elseif ($parsed['protocol'] == 'unix') { + $parsed['socket'] = $proto_opts; + } + + // Get dabase if any + // $dsn => database + if ($dsn) { + // /database + if (($pos = strpos($dsn, '?')) === false) { + $parsed['database'] = rawurldecode($dsn); + // /database?param1=value1¶m2=value2 + } else { + $parsed['database'] = rawurldecode(substr($dsn, 0, $pos)); + $dsn = substr($dsn, $pos + 1); + if (strpos($dsn, '&') !== false) { + $opts = explode('&', $dsn); + } else { // database?param1=value1 + $opts = array($dsn); + } + foreach ($opts as $opt) { + list($key, $value) = explode('=', $opt); + if (!isset($parsed[$key])) { + // don't allow params overwrite + $parsed[$key] = rawurldecode($value); + } + } + } + } + + return $parsed; + } + + // }}} + // {{{ assertExtension() + + /** + * Load a PHP database extension if it is not loaded already. + * + * @access public + * + * @param string $name the base name of the extension (without the .so or + * .dll suffix) + * + * @return boolean true if the extension was already or successfully + * loaded, false if it could not be loaded + */ + function assertExtension($name) + { + if (!extension_loaded($name)) { + $dlext = OS_WINDOWS ? '.dll' : '.so'; + $dlprefix = OS_WINDOWS ? 'php_' : ''; + @dl($dlprefix . $name . $dlext); + return extension_loaded($name); + } + return true; + } + // }}} +} + +// }}} +// {{{ class DB_Error + +/** + * DB_Error implements a class for reporting portable database error + * messages. + * + * @package DB + * @author Stig Bakken + */ +class DB_Error extends PEAR_Error +{ + // {{{ constructor + + /** + * DB_Error constructor. + * + * @param mixed $code DB error code, or string with error message. + * @param integer $mode what "error mode" to operate in + * @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER + * @param mixed $debuginfo additional debug info, such as the last query + * + * @access public + * + * @see PEAR_Error + */ + function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN, + $level = E_USER_NOTICE, $debuginfo = null) + { + if (is_int($code)) { + $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, $mode, $level, $debuginfo); + } else { + $this->PEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo); + } + } + // }}} +} + +// }}} +// {{{ class DB_result + +/** + * This class implements a wrapper for a DB result set. + * A new instance of this class will be returned by the DB implementation + * after processing a query that returns data. + * + * @package DB + * @author Stig Bakken + */ +class DB_result +{ + // {{{ properties + + var $dbh; + var $result; + var $row_counter = null; + + /** + * for limit queries, the row to start fetching + * @var integer + */ + var $limit_from = null; + + /** + * for limit queries, the number of rows to fetch + * @var integer + */ + var $limit_count = null; + + // }}} + // {{{ constructor + + /** + * DB_result constructor. + * @param resource &$dbh DB object reference + * @param resource $result result resource id + * @param array $options assoc array with optional result options + */ + function DB_result(&$dbh, $result, $options = array()) + { + $this->dbh = &$dbh; + $this->result = $result; + foreach ($options as $key => $value) { + $this->setOption($key, $value); + } + $this->limit_type = $dbh->features['limit']; + $this->autofree = $dbh->options['autofree']; + $this->fetchmode = $dbh->fetchmode; + $this->fetchmode_object_class = $dbh->fetchmode_object_class; + } + + function setOption($key, $value = null) + { + switch ($key) { + case 'limit_from': + $this->limit_from = $value; break; + case 'limit_count': + $this->limit_count = $value; break; + } + } + + // }}} + // {{{ fetchRow() + + /** + * Fetch a row of data and return it by reference into an array. + * + * The type of array returned can be controlled either by setting this + * method's $fetchmode parameter or by changing the default + * fetch mode setFetchMode() before calling this method. + * + * There are two options for standardizing the information returned + * from databases, ensuring their values are consistent when changing + * DBMS's. These portability options can be turned on when creating a + * new DB object or by using setOption(). + * + * + DB_PORTABILITY_LOWERCASE + * convert names of fields to lower case + * + * + DB_PORTABILITY_RTRIM + * right trim the data + * + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return array a row of data, null on no more rows or PEAR_Error + * object on error + * + * @see DB_common::setOption(), DB_common::setFetchMode() + * @access public + */ + function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null) + { + if ($fetchmode === DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + if ($fetchmode === DB_FETCHMODE_OBJECT) { + $fetchmode = DB_FETCHMODE_ASSOC; + $object_class = $this->fetchmode_object_class; + } + if ($this->limit_from !== null) { + if ($this->row_counter === null) { + $this->row_counter = $this->limit_from; + // Skip rows + if ($this->limit_type == false) { + $i = 0; + while ($i++ < $this->limit_from) { + $this->dbh->fetchInto($this->result, $arr, $fetchmode); + } + } + } + if ($this->row_counter >= ( + $this->limit_from + $this->limit_count)) + { + if ($this->autofree) { + $this->free(); + } + $tmp = null; + return $tmp; + } + if ($this->limit_type == 'emulate') { + $rownum = $this->row_counter; + } + $this->row_counter++; + } + $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); + if ($res === DB_OK) { + if (isset($object_class)) { + // default mode specified in DB_common::fetchmode_object_class property + if ($object_class == 'stdClass') { + $arr = (object) $arr; + } else { + $arr = &new $object_class($arr); + } + } + return $arr; + } + if ($res == null && $this->autofree) { + $this->free(); + } + return $res; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row of data into an array which is passed by reference. + * + * The type of array returned can be controlled either by setting this + * method's $fetchmode parameter or by changing the default + * fetch mode setFetchMode() before calling this method. + * + * There are two options for standardizing the information returned + * from databases, ensuring their values are consistent when changing + * DBMS's. These portability options can be turned on when creating a + * new DB object or by using setOption(). + * + * + DB_PORTABILITY_LOWERCASE + * convert names of fields to lower case + * + * + DB_PORTABILITY_RTRIM + * right trim the data + * + * @param array &$arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null on no more rows or + * a DB_Error object on error + * + * @see DB_common::setOption(), DB_common::setFetchMode() + * @access public + */ + function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null) + { + if ($fetchmode === DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + if ($fetchmode === DB_FETCHMODE_OBJECT) { + $fetchmode = DB_FETCHMODE_ASSOC; + $object_class = $this->fetchmode_object_class; + } + if ($this->limit_from !== null) { + if ($this->row_counter === null) { + $this->row_counter = $this->limit_from; + // Skip rows + if ($this->limit_type == false) { + $i = 0; + while ($i++ < $this->limit_from) { + $this->dbh->fetchInto($this->result, $arr, $fetchmode); + } + } + } + if ($this->row_counter >= ( + $this->limit_from + $this->limit_count)) + { + if ($this->autofree) { + $this->free(); + } + return null; + } + if ($this->limit_type == 'emulate') { + $rownum = $this->row_counter; + } + + $this->row_counter++; + } + $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); + if ($res === DB_OK) { + if (isset($object_class)) { + // default mode specified in DB_common::fetchmode_object_class property + if ($object_class == 'stdClass') { + $arr = (object) $arr; + } else { + $arr = new $object_class($arr); + } + } + return DB_OK; + } + if ($res == null && $this->autofree) { + $this->free(); + } + return $res; + } + + // }}} + // {{{ numCols() + + /** + * Get the the number of columns in a result set. + * + * @return int the number of columns, or a DB error + * + * @access public + */ + function numCols() + { + return $this->dbh->numCols($this->result); + } + + // }}} + // {{{ numRows() + + /** + * Get the number of rows in a result set. + * + * @return int the number of rows, or a DB error + * + * @access public + */ + function numRows() + { + return $this->dbh->numRows($this->result); + } + + // }}} + // {{{ nextResult() + + /** + * Get the next result if a batch of queries was executed. + * + * @return bool true if a new result is available or false if not. + * + * @access public + */ + function nextResult() + { + return $this->dbh->nextResult($this->result); + } + + // }}} + // {{{ free() + + /** + * Frees the resources allocated for this result set. + * @return int error code + * + * @access public + */ + function free() + { + $err = $this->dbh->freeResult($this->result); + if (DB::isError($err)) { + return $err; + } + $this->result = false; + return true; + } + + // }}} + // {{{ tableInfo() + + /** + * @deprecated + * @internal + * @see DB_common::tableInfo() + */ + function tableInfo($mode = null) + { + if (is_string($mode)) { + return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA); + } + return $this->dbh->tableInfo($this, $mode); + } + + // }}} + // {{{ getRowCounter() + + /** + * returns the actual row number + * @return integer + */ + function getRowCounter() + { + return $this->row_counter; + } + // }}} +} + +// }}} +// {{{ class DB_row + +/** + * Pear DB Row Object + * @see DB_common::setFetchMode() + */ +class DB_row +{ + // {{{ constructor + + /** + * constructor + * + * @param resource row data as array + */ + function DB_row(&$arr) + { + foreach ($arr as $key => $value) { + $this->$key = &$arr[$key]; + } + } + + // }}} +} + +// }}} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/common.php b/thirdparty/pear/DB/common.php new file mode 100644 index 0000000..868a101 --- /dev/null +++ b/thirdparty/pear/DB/common.php @@ -0,0 +1,2042 @@ + | +// | Tomas V.V.Cox | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once 'PEAR.php'; + +/** + * DB_common is a base class for DB implementations, and must be + * inherited by all such + * + * @package DB + * @version $Id$ + * @category Database + * @author Stig Bakken + * @author Tomas V.V.Cox + */ +class DB_common extends PEAR +{ + // {{{ properties + + /** + * assoc of capabilities for this DB implementation + * $features['limit'] => 'emulate' => emulate with fetch row by number + * 'alter' => alter the query + * false => skip rows + * @var array + */ + var $features = array(); + + /** + * assoc mapping native error codes to DB ones + * @var array + */ + var $errorcode_map = array(); + + /** + * DB type (mysql, oci8, odbc etc.) + * @var string + */ + var $phptype; + + /** + * @var string + */ + var $prepare_tokens; + + /** + * @var string + */ + var $prepare_types; + + /** + * @var string + */ + var $prepared_queries; + + /** + * @var integer + */ + var $prepare_maxstmt = 0; + + /** + * @var string + */ + var $last_query = ''; + + /** + * @var integer + */ + var $fetchmode = DB_FETCHMODE_ORDERED; + + /** + * @var string + */ + var $fetchmode_object_class = 'stdClass'; + + /** + * Run-time configuration options. + * + * The 'optimize' option has been deprecated. Use the 'portability' + * option instead. + * + * @see DB_common::setOption() + * @var array + */ + var $options = array( + 'persistent' => false, + 'ssl' => false, + 'debug' => 0, + 'seqname_format' => '%s_seq', + 'autofree' => false, + 'portability' => DB_PORTABILITY_NONE, + 'optimize' => 'performance', // Deprecated. Use 'portability'. + ); + + /** + * DB handle + * @var resource + */ + var $dbh; + + // }}} + // {{{ toString() + + /** + * String conversation + * + * @return string + * @access private + */ + function toString() + { + $info = strtolower(get_class($this)); + $info .= ': (phptype=' . $this->phptype . + ', dbsyntax=' . $this->dbsyntax . + ')'; + + if ($this->connection) { + $info .= ' [connected]'; + } + + return $info; + } + + // }}} + // {{{ constructor + + /** + * Constructor + */ + function DB_common() + { + $this->PEAR('DB_Error'); + } + + // }}} + // {{{ quoteString() + + /** + * DEPRECATED: Quotes a string so it can be safely used within string + * delimiters in a query + * + * @return string quoted string + * + * @see DB_common::quoteSmart(), DB_common::escapeSimple() + * @deprecated Deprecated in release 1.2 or lower + * @internal + */ + function quoteString($string) + { + $string = $this->quote($string); + if ($string{0} == "'") { + return substr($string, 1, -1); + } + return $string; + } + + // }}} + // {{{ quote() + + /** + * DEPRECATED: Quotes a string so it can be safely used in a query + * + * @param string $string the input string to quote + * + * @return string The NULL string or the string quotes + * in magic_quote_sybase style + * + * @see DB_common::quoteSmart(), DB_common::escapeSimple() + * @deprecated Deprecated in release 1.6.0 + * @internal + */ + function quote($string = null) + { + return ($string === null) ? 'NULL' : "'".str_replace("'", "''", $string)."'"; + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quote a string so it can be safely used as a table or column name + * + * Delimiting style depends on which database driver is being used. + * + * NOTE: just because you CAN use delimited identifiers doesn't mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * Portability is broken by using the following characters inside + * delimited identifiers: + * + backtick (`) -- due to MySQL + * + double quote (") -- due to Oracle + * + brackets ([ or ]) -- due to Access + * + * Delimited identifiers are known to generally work correctly under + * the following drivers: + * + mssql + * + mysql + * + mysqli + * + oci8 + * + odbc(access) + * + odbc(db2) + * + pgsql + * + sqlite + * + sybase + * + * InterBase doesn't seem to be able to use delimited identifiers + * via PHP 4. They work fine under PHP 5. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @since 1.6.0 + * @access public + */ + function quoteIdentifier($str) + { + return '"' . str_replace('"', '""', $str) . '"'; + } + + // }}} + // {{{ quoteSmart() + + /** + * Format input so it can be safely used in a query + * + * The output depends on the PHP data type of input and the database + * type being used. + * + * @param mixed $in data to be quoted + * + * @return mixed the format of the results depends on the input's + * PHP type: + * + *
    + *
  • + * input -> returns + *
  • + *
  • + * null -> the string NULL + *
  • + *
  • + * integer or double -> the unquoted number + *
  • + *
  • + * &type.bool; -> output depends on the driver in use + * Most drivers return integers: 1 if + * true or 0 if + * false. + * Some return strings: TRUE if + * true or FALSE if + * false. + * Finally one returns strings: T if + * true or F if + * false. Here is a list of each DBMS, + * the values returned and the suggested column type: + *
      + *
    • + * dbase -> T/F + * (Logical) + *
    • + *
    • + * fbase -> TRUE/FALSE + * (BOOLEAN) + *
    • + *
    • + * ibase -> 1/0 + * (SMALLINT) [1] + *
    • + *
    • + * ifx -> 1/0 + * (SMALLINT) [1] + *
    • + *
    • + * msql -> 1/0 + * (INTEGER) + *
    • + *
    • + * mssql -> 1/0 + * (BIT) + *
    • + *
    • + * mysql -> 1/0 + * (TINYINT(1)) + *
    • + *
    • + * mysqli -> 1/0 + * (TINYINT(1)) + *
    • + *
    • + * oci8 -> 1/0 + * (NUMBER(1)) + *
    • + *
    • + * odbc -> 1/0 + * (SMALLINT) [1] + *
    • + *
    • + * pgsql -> TRUE/FALSE + * (BOOLEAN) + *
    • + *
    • + * sqlite -> 1/0 + * (INTEGER) + *
    • + *
    • + * sybase -> 1/0 + * (TINYINT(1)) + *
    • + *
    + * [1] Accommodate the lowest common denominator because not all + * versions of have BOOLEAN. + *
  • + *
  • + * other (including strings and numeric strings) -> + * the data with single quotes escaped by preceeding + * single quotes, backslashes are escaped by preceeding + * backslashes, then the whole string is encapsulated + * between single quotes + *
  • + *
+ * + * @since 1.6.0 + * @see DB_common::escapeSimple() + * @access public + */ + function quoteSmart($in) + { + if (is_int($in) || is_double($in)) { + return $in; + } elseif (is_bool($in)) { + return $in ? 1 : 0; + } elseif (is_null($in)) { + return 'NULL'; + } else { + return "'" . $this->escapeSimple($in) . "'"; + } + } + + // }}} + // {{{ escapeSimple() + + /** + * Escape a string according to the current DBMS's standards + * + * In SQLite, this makes things safe for inserts/updates, but may + * cause problems when performing text comparisons against columns + * containing binary data. See the + * {@link http://php.net/sqlite_escape_string PHP manual} for more info. + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @since 1.6.0 + * @see DB_common::quoteSmart() + * @access public + */ + function escapeSimple($str) { + return str_replace("'", "''", $str); + } + + // }}} + // {{{ provides() + + /** + * Tell whether a DB implementation or its backend extension + * supports a given feature + * + * @param array $feature name of the feature (see the DB class doc) + * @return bool whether this DB implementation supports $feature + * @access public + */ + function provides($feature) + { + return $this->features[$feature]; + } + + // }}} + // {{{ errorCode() + + /** + * Map native error codes to DB's portable ones + * + * Requires that the DB implementation's constructor fills + * in the $errorcode_map property. + * + * @param mixed $nativecode the native error code, as returned by the + * backend database extension (string or integer) + * + * @return int a portable DB error code, or DB_ERROR if this DB + * implementation has no mapping for the given error code. + * + * @access public + */ + function errorCode($nativecode) + { + if (isset($this->errorcode_map[$nativecode])) { + return $this->errorcode_map[$nativecode]; + } + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; + } + + // }}} + // {{{ errorMessage() + + /** + * Map a DB error code to a textual message. This is actually + * just a wrapper for DB::errorMessage() + * + * @param integer $dbcode the DB error code + * + * @return string the corresponding error message, of false + * if the error code was unknown + * + * @access public + */ + function errorMessage($dbcode) + { + return DB::errorMessage($this->errorcode_map[$dbcode]); + } + + // }}} + // {{{ raiseError() + + /** + * Communicate an error and invoke error callbacks, etc + * + * Basically a wrapper for PEAR::raiseError without the message string. + * + * @param mixed integer error code, or a PEAR error object (all + * other parameters are ignored if this parameter is + * an object + * + * @param int error mode, see PEAR_Error docs + * + * @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the + * error level (E_USER_NOTICE etc). If error mode is + * PEAR_ERROR_CALLBACK, this is the callback function, + * either as a function name, or as an array of an + * object and method name. For other error modes this + * parameter is ignored. + * + * @param string Extra debug information. Defaults to the last + * query and native error code. + * + * @param mixed Native error code, integer or string depending the + * backend. + * + * @return object a PEAR error object + * + * @access public + * @see PEAR_Error + */ + function &raiseError($code = DB_ERROR, $mode = null, $options = null, + $userinfo = null, $nativecode = null) + { + // The error is yet a DB error object + if (is_object($code)) { + // because we the static PEAR::raiseError, our global + // handler should be used if it is set + if ($mode === null && !empty($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + } + $tmp = PEAR::raiseError($code, null, $mode, $options, null, null, true); + return $tmp; + } + + if ($userinfo === null) { + $userinfo = $this->last_query; + } + + if ($nativecode) { + $userinfo .= ' [nativecode=' . trim($nativecode) . ']'; + } + + $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo, + 'DB_Error', true); + return $tmp; + } + + // }}} + // {{{ setFetchMode() + + /** + * Sets which fetch mode should be used by default on queries + * on this connection + * + * @param integer $fetchmode DB_FETCHMODE_ORDERED or + * DB_FETCHMODE_ASSOC, possibly bit-wise OR'ed with + * DB_FETCHMODE_FLIPPED. + * + * @param string $object_class The class of the object + * to be returned by the fetch methods when + * the DB_FETCHMODE_OBJECT mode is selected. + * If no class is specified by default a cast + * to object from the assoc array row will be done. + * There is also the posibility to use and extend the + * 'DB_row' class. + * + * @see DB_FETCHMODE_ORDERED + * @see DB_FETCHMODE_ASSOC + * @see DB_FETCHMODE_FLIPPED + * @see DB_FETCHMODE_OBJECT + * @see DB_row::DB_row() + * @access public + */ + function setFetchMode($fetchmode, $object_class = 'stdClass') + { + switch ($fetchmode) { + case DB_FETCHMODE_OBJECT: + $this->fetchmode_object_class = $object_class; + case DB_FETCHMODE_ORDERED: + case DB_FETCHMODE_ASSOC: + $this->fetchmode = $fetchmode; + break; + default: + return $this->raiseError('invalid fetchmode mode'); + } + } + + // }}} + // {{{ setOption() + + /** + * Set run-time configuration options for PEAR DB + * + * Options, their data types, default values and description: + *
    + *
  • + * autofree boolean = false + *
    should results be freed automatically when there are no + * more rows? + *
  • + * debug integer = 0 + *
    debug level + *
  • + * persistent boolean = false + *
    should the connection be persistent? + *
  • + * portability integer = DB_PORTABILITY_NONE + *
    portability mode constant (see below) + *
  • + * seqname_format string = %s_seq + *
    the sprintf() format string used on sequence names. This + * format is applied to sequence names passed to + * createSequence(), nextID() and dropSequence(). + *
  • + * ssl boolean = false + *
    use ssl to connect? + *
  • + *
+ * + * ----------------------------------------- + * + * PORTABILITY MODES + * + * These modes are bitwised, so they can be combined using | + * and removed using ^. See the examples section below on how + * to do this. + * + * DB_PORTABILITY_NONE + * turn off all portability features + * + * This mode gets automatically turned on if the deprecated + * optimize option gets set to performance. + * + * + * DB_PORTABILITY_LOWERCASE + * convert names of tables and fields to lower case when using + * get*(), fetch*() and tableInfo() + * + * This mode gets automatically turned on in the following databases + * if the deprecated option optimize gets set to + * portability: + * + oci8 + * + * + * DB_PORTABILITY_RTRIM + * right trim the data output by get*() fetch*() + * + * + * DB_PORTABILITY_DELETE_COUNT + * force reporting the number of rows deleted + * + * Some DBMS's don't count the number of rows deleted when performing + * simple DELETE FROM tablename queries. This portability + * mode tricks such DBMS's into telling the count by adding + * WHERE 1=1 to the end of DELETE queries. + * + * This mode gets automatically turned on in the following databases + * if the deprecated option optimize gets set to + * portability: + * + fbsql + * + mysql + * + mysqli + * + sqlite + * + * + * DB_PORTABILITY_NUMROWS + * enable hack that makes numRows() work in Oracle + * + * This mode gets automatically turned on in the following databases + * if the deprecated option optimize gets set to + * portability: + * + oci8 + * + * + * DB_PORTABILITY_ERRORS + * makes certain error messages in certain drivers compatible + * with those from other DBMS's + * + * + mysql, mysqli: change unique/primary key constraints + * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT + * + * + odbc(access): MS's ODBC driver reports 'no such field' as code + * 07001, which means 'too few parameters.' When this option is on + * that code gets mapped to DB_ERROR_NOSUCHFIELD. + * DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD + * + * + * DB_PORTABILITY_NULL_TO_EMPTY + * convert null values to empty strings in data output by get*() and + * fetch*(). Needed because Oracle considers empty strings to be null, + * while most other DBMS's know the difference between empty and null. + * + * + * DB_PORTABILITY_ALL + * turn on all portability features + * + * ----------------------------------------- + * + * Example 1. Simple setOption() example + * setOption('autofree', true); + * ?> + * + * Example 2. Portability for lowercasing and trimming + * setOption('portability', + * DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM); + * ?> + * + * Example 3. All portability options except trimming + * setOption('portability', + * DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM); + * ?> + * + * @param string $option option name + * @param mixed $value value for the option + * + * @return int DB_OK on success. DB_Error object on failure. + * + * @see DB_common::$options + */ + function setOption($option, $value) + { + if (isset($this->options[$option])) { + $this->options[$option] = $value; + + /* + * Backwards compatibility check for the deprecated 'optimize' + * option. Done here in case settings change after connecting. + */ + if ($option == 'optimize') { + if ($value == 'portability') { + switch ($this->phptype) { + case 'oci8': + $this->options['portability'] = + DB_PORTABILITY_LOWERCASE | + DB_PORTABILITY_NUMROWS; + break; + case 'fbsql': + case 'mysql': + case 'mysqli': + case 'sqlite': + $this->options['portability'] = + DB_PORTABILITY_DELETE_COUNT; + break; + } + } else { + $this->options['portability'] = DB_PORTABILITY_NONE; + } + } + + return DB_OK; + } + return $this->raiseError("unknown option $option"); + } + + // }}} + // {{{ getOption() + + /** + * Returns the value of an option + * + * @param string $option option name + * + * @return mixed the option value + */ + function getOption($option) + { + if (isset($this->options[$option])) { + return $this->options[$option]; + } + return $this->raiseError("unknown option $option"); + } + + // }}} + // {{{ prepare() + + /** + * Prepares a query for multiple execution with execute() + * + * Creates a query that can be run multiple times. Each time it is run, + * the placeholders, if any, will be replaced by the contents of + * execute()'s $data argument. + * + * Three types of placeholders can be used: + * + ? scalar value (i.e. strings, integers). The system + * will automatically quote and escape the data. + * + ! value is inserted 'as is' + * + & requires a file name. The file's contents get + * inserted into the query (i.e. saving binary + * data in a db) + * + * Example 1. + * prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); + * $data = array( + * "John's text", + * "'it''s good'", + * 'filename.txt' + * ); + * $res = $dbh->execute($sth, $data); + * ?> + * + * Use backslashes to escape placeholder characters if you don't want + * them to be interpreted as placeholders: + *
+     *    "UPDATE foo SET col=? WHERE col='over \& under'"
+     * 
+ * + * With some database backends, this is emulated. + * + * {@internal ibase and oci8 have their own prepare() methods.}} + * + * @param string $query query to be prepared + * + * @return mixed DB statement resource on success. DB_Error on failure. + * + * @see DB_common::execute() + * @access public + */ + function prepare($query) + { + $tokens = preg_split('/((?prepare_tokens[] = &$newtokens; + end($this->prepare_tokens); + + $k = key($this->prepare_tokens); + $this->prepare_types[$k] = $types; + $this->prepared_queries[$k] = implode(' ', $newtokens); + + return $k; + } + + // }}} + // {{{ autoPrepare() + + /** + * Automaticaly generate an insert or update query and pass it to prepare() + * + * @param string $table name of the table + * @param array $table_fields ordered array containing the fields names + * @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE) + * @param string $where in case of update queries, this string will be put after the sql WHERE statement + * @return resource handle for the query + * @see DB_common::prepare(), DB_common::buildManipSQL() + * @access public + */ + function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, $where = false) + { + $query = $this->buildManipSQL($table, $table_fields, $mode, $where); + return $this->prepare($query); + } + + // }}} + // {{{ autoExecute() + + /** + * Automaticaly generate an insert or update query and call prepare() + * and execute() with it + * + * @param string $table name of the table + * @param array $fields_values assoc ($key=>$value) where $key is a field name and $value its value + * @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE) + * @param string $where in case of update queries, this string will be put after the sql WHERE statement + * @return mixed a new DB_Result or a DB_Error when fail + * @see DB_common::autoPrepare(), DB_common::buildManipSQL() + * @access public + */ + function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, $where = false) + { + $sth = $this->autoPrepare($table, array_keys($fields_values), $mode, $where); + $ret =& $this->execute($sth, array_values($fields_values)); + $this->freePrepared($sth); + return $ret; + + } + + // }}} + // {{{ buildManipSQL() + + /** + * Make automaticaly an sql query for prepare() + * + * Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), DB_AUTOQUERY_INSERT) + * will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?) + * NB : - This belongs more to a SQL Builder class, but this is a simple facility + * - Be carefull ! If you don't give a $where param with an UPDATE query, all + * the records of the table will be updated ! + * + * @param string $table name of the table + * @param array $table_fields ordered array containing the fields names + * @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE) + * @param string $where in case of update queries, this string will be put after the sql WHERE statement + * @return string sql query for prepare() + * @access public + */ + function buildManipSQL($table, $table_fields, $mode, $where = false) + { + if (count($table_fields) == 0) { + $this->raiseError(DB_ERROR_NEED_MORE_DATA); + } + $first = true; + switch ($mode) { + case DB_AUTOQUERY_INSERT: + $values = ''; + $names = ''; + foreach ($table_fields as $value) { + if ($first) { + $first = false; + } else { + $names .= ','; + $values .= ','; + } + $names .= $value; + $values .= '?'; + } + return "INSERT INTO $table ($names) VALUES ($values)"; + case DB_AUTOQUERY_UPDATE: + $set = ''; + foreach ($table_fields as $value) { + if ($first) { + $first = false; + } else { + $set .= ','; + } + $set .= "$value = ?"; + } + $sql = "UPDATE $table SET $set"; + if ($where) { + $sql .= " WHERE $where"; + } + return $sql; + default: + $this->raiseError(DB_ERROR_SYNTAX); + } + } + + // }}} + // {{{ execute() + + /** + * Executes a DB statement prepared with prepare() + * + * Example 1. + * prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); + * $data = array( + * "John's text", + * "'it''s good'", + * 'filename.txt' + * ); + * $res =& $dbh->execute($sth, $data); + * ?> + * + * @param resource $stmt a DB statement resource returned from prepare() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return object a new DB_Result or a DB_Error when fail + * + * {@internal ibase and oci8 have their own execute() methods.}} + * + * @see DB_common::prepare() + * @access public + */ + function &execute($stmt, $data = array()) + { + $realquery = $this->executeEmulateQuery($stmt, $data); + if (DB::isError($realquery)) { + return $realquery; + } + $result = $this->simpleQuery($realquery); + + if (DB::isError($result) || $result === DB_OK) { + return $result; + } else { + $tmp =& new DB_result($this, $result); + return $tmp; + } + } + + // }}} + // {{{ executeEmulateQuery() + + /** + * Emulates the execute statement, when not supported + * + * @param resource $stmt a DB statement resource returned from execute() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed a string containing the real query run when emulating + * prepare/execute. A DB error code is returned on failure. + * + * @see DB_common::execute() + * @access private + */ + function executeEmulateQuery($stmt, $data = array()) + { + $stmt = (int)$stmt; + if (!is_array($data)) { + $data = array($data); + } + + if (count($this->prepare_types[$stmt]) != count($data)) { + $this->last_query = $this->prepared_queries[$stmt]; + return $this->raiseError(DB_ERROR_MISMATCH); + } + + $realquery = $this->prepare_tokens[$stmt][0]; + + $i = 0; + foreach ($data as $value) { + if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) { + $realquery .= $this->quoteSmart($value); + } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) { + $fp = @fopen($value, 'rb'); + if (!$fp) { + return $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + } + $realquery .= $this->quoteSmart(fread($fp, filesize($value))); + fclose($fp); + } else { + $realquery .= $value; + } + + $realquery .= $this->prepare_tokens[$stmt][++$i]; + } + + return $realquery; + } + + // }}} + // {{{ executeMultiple() + + /** + * This function does several execute() calls on the same + * statement handle + * + * $data must be an array indexed numerically + * from 0, one execute call is done for every "row" in the array. + * + * If an error occurs during execute(), executeMultiple() does not + * execute the unfinished rows, but rather returns that error. + * + * @param resource $stmt query handle from prepare() + * @param array $data numeric array containing the + * data to insert into the query + * + * @return mixed DB_OK or DB_Error + * + * @see DB_common::prepare(), DB_common::execute() + * @access public + */ + function executeMultiple($stmt, $data) + { + foreach ($data as $value) { + $res =& $this->execute($stmt, $value); + if (DB::isError($res)) { + return $res; + } + } + return DB_OK; + } + + // }}} + // {{{ freePrepared() + + /** + * Free the resource used in a prepared query + * + * @param $stmt The resurce returned by the prepare() function + * @see DB_common::prepare() + */ + function freePrepared($stmt) + { + $stmt = (int)$stmt; + // Free the internal prepared vars + if (isset($this->prepare_tokens[$stmt])) { + unset($this->prepare_tokens[$stmt]); + unset($this->prepare_types[$stmt]); + unset($this->prepared_queries[$stmt]); + return true; + } + return false; + } + + // }}} + // {{{ modifyQuery() + + /** + * This method is used by backends to alter queries for various + * reasons + * + * It is defined here to assure that all implementations + * have this method defined. + * + * @param string $query query to modify + * + * @return the new (modified) query + * + * @access private + */ + function modifyQuery($query) { + return $query; + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * This method is used by backends to alter limited queries + * + * @param string $query query to modify + * @param integer $from the row to start to fetching + * @param integer $count the numbers of rows to fetch + * + * @return the new (modified) query + * + * @access private + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + return $query; + } + + // }}} + // {{{ query() + + /** + * Send a query to the database and return any results with a + * DB_result object + * + * The query string can be either a normal statement to be sent directly + * to the server OR if $params are passed the query can have + * placeholders and it will be passed through prepare() and execute(). + * + * @param string $query the SQL query or the statement to prepare + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed a DB_result object or DB_OK on success, a DB + * error on failure + * + * @see DB_result, DB_common::prepare(), DB_common::execute() + * @access public + */ + function &query($query, $params = array()) + { + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + if (DB::isError($sth)) { + return $sth; + } + $ret =& $this->execute($sth, $params); + $this->freePrepared($sth); + return $ret; + } else { + $result = $this->simpleQuery($query); + if (DB::isError($result) || $result === DB_OK) { + return $result; + } else { + $tmp =& new DB_result($this, $result); + return $tmp; + } + } + } + + // }}} + // {{{ limitQuery() + + /** + * Generates a limited query + * + * @param string $query query + * @param integer $from the row to start to fetching + * @param integer $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed a DB_Result object, DB_OK or a DB_Error + * + * @access public + */ + function &limitQuery($query, $from, $count, $params = array()) + { + $query = $this->modifyLimitQuery($query, $from, $count, $params); + if (DB::isError($query)){ + return $query; + } + $result =& $this->query($query, $params); + if (is_a($result, 'DB_result')) { + $result->setOption('limit_from', $from); + $result->setOption('limit_count', $count); + } + return $result; + } + + // }}} + // {{{ getOne() + + /** + * Fetch the first column of the first row of data returned from + * a query + * + * Takes care of doing the query and freeing the results when finished. + * + * @param string $query the SQL query + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed the returned value of the query. DB_Error on failure. + * + * @access public + */ + function &getOne($query, $params = array()) + { + settype($params, 'array'); + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + if (DB::isError($sth)) { + return $sth; + } + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if (DB::isError($res)) { + return $res; + } + + $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED); + $res->free(); + + if ($err !== DB_OK) { + return $err; + } + + return $row[0]; + } + + // }}} + // {{{ getRow() + + /** + * Fetch the first row of data returned from a query + * + * Takes care of doing the query and freeing the results when finished. + * + * @param string $query the SQL query + * @param array $params array to be used in execution of the statement. + * Quantity of array elements must match quantity + * of placeholders in query. This function does + * NOT support scalars. + * @param int $fetchmode the fetch mode to use + * + * @return array the first row of results as an array indexed from + * 0, or a DB error code. + * + * @access public + */ + function &getRow($query, + $params = array(), + $fetchmode = DB_FETCHMODE_DEFAULT) + { + // compat check, the params and fetchmode parameters used to + // have the opposite order + if (!is_array($params)) { + if (is_array($fetchmode)) { + if ($params === null) { + $tmp = DB_FETCHMODE_DEFAULT; + } else { + $tmp = $params; + } + $params = $fetchmode; + $fetchmode = $tmp; + } elseif ($params !== null) { + $fetchmode = $params; + $params = array(); + } + } + + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + if (DB::isError($sth)) { + return $sth; + } + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if (DB::isError($res)) { + return $res; + } + + $err = $res->fetchInto($row, $fetchmode); + + $res->free(); + + if ($err !== DB_OK) { + return $err; + } + + return $row; + } + + // }}} + // {{{ getCol() + + /** + * Fetch a single column from a result set and return it as an + * indexed array + * + * @param string $query the SQL query + * @param mixed $col which column to return (integer [column number, + * starting at 0] or string [column name]) + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return array an indexed array with the data from the first + * row at index 0, or a DB error code + * + * @see DB_common::query() + * @access public + */ + function &getCol($query, $col = 0, $params = array()) + { + settype($params, 'array'); + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + + if (DB::isError($sth)) { + return $sth; + } + + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if (DB::isError($res)) { + return $res; + } + + $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC; + + if (!is_array($row = $res->fetchRow($fetchmode))) { + $ret = array(); + } else { + if (!array_key_exists($col, $row)) { + $ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD); + } else { + $ret = array($row[$col]); + while (is_array($row = $res->fetchRow($fetchmode))) { + $ret[] = $row[$col]; + } + } + } + + $res->free(); + + if (DB::isError($row)) { + $ret = $row; + } + + return $ret; + } + + // }}} + // {{{ getAssoc() + + /** + * Fetch the entire result set of a query and return it as an + * associative array using the first column as the key + * + * If the result set contains more than two columns, the value + * will be an array of the values from column 2-n. If the result + * set contains only two columns, the returned value will be a + * scalar with the value of the second column (unless forced to an + * array with the $force_array parameter). A DB error code is + * returned on errors. If the result set contains fewer than two + * columns, a DB_ERROR_TRUNCATED error is returned. + * + * For example, if the table "mytable" contains: + * + *
+     *  ID      TEXT       DATE
+     * --------------------------------
+     *  1       'one'      944679408
+     *  2       'two'      944679408
+     *  3       'three'    944679408
+     * 
+ * + * Then the call getAssoc('SELECT id,text FROM mytable') returns: + *
+     *   array(
+     *     '1' => 'one',
+     *     '2' => 'two',
+     *     '3' => 'three',
+     *   )
+     * 
+ * + * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns: + *
+     *   array(
+     *     '1' => array('one', '944679408'),
+     *     '2' => array('two', '944679408'),
+     *     '3' => array('three', '944679408')
+     *   )
+     * 
+ * + * If the more than one row occurs with the same value in the + * first column, the last row overwrites all previous ones by + * default. Use the $group parameter if you don't want to + * overwrite like this. Example: + * + *
+     * getAssoc('SELECT category,id,name FROM mytable', false, null,
+     *          DB_FETCHMODE_ASSOC, true) returns:
+     *
+     *   array(
+     *     '1' => array(array('id' => '4', 'name' => 'number four'),
+     *                  array('id' => '6', 'name' => 'number six')
+     *            ),
+     *     '9' => array(array('id' => '4', 'name' => 'number four'),
+     *                  array('id' => '6', 'name' => 'number six')
+     *            )
+     *   )
+     * 
+ * + * Keep in mind that database functions in PHP usually return string + * values for results regardless of the database's internal type. + * + * @param string $query the SQL query + * @param boolean $force_array used only when the query returns + * exactly two columns. If true, the values + * of the returned array will be one-element + * arrays instead of scalars. + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * @param int $fetchmode the fetch mode to use + * @param boolean $group if true, the values of the returned array + * is wrapped in another array. If the same + * key value (in the first column) repeats + * itself, the values will be appended to + * this array instead of overwriting the + * existing values. + * + * @return array associative array with results from the query. + * DB Error on failure. + * + * @access public + */ + function &getAssoc($query, $force_array = false, $params = array(), + $fetchmode = DB_FETCHMODE_DEFAULT, $group = false) + { + settype($params, 'array'); + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + + if (DB::isError($sth)) { + return $sth; + } + + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if (DB::isError($res)) { + return $res; + } + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + $cols = $res->numCols(); + + if ($cols < 2) { + $tmp =& $this->raiseError(DB_ERROR_TRUNCATED); + return $tmp; + } + + $results = array(); + + if ($cols > 2 || $force_array) { + // return array values + // XXX this part can be optimized + if ($fetchmode == DB_FETCHMODE_ASSOC) { + while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) { + reset($row); + $key = current($row); + unset($row[key($row)]); + if ($group) { + $results[$key][] = $row; + } else { + $results[$key] = $row; + } + } + } elseif ($fetchmode == DB_FETCHMODE_OBJECT) { + while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) { + $arr = get_object_vars($row); + $key = current($arr); + if ($group) { + $results[$key][] = $row; + } else { + $results[$key] = $row; + } + } + } else { + while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { + // we shift away the first element to get + // indices running from 0 again + $key = array_shift($row); + if ($group) { + $results[$key][] = $row; + } else { + $results[$key] = $row; + } + } + } + if (DB::isError($row)) { + $results = $row; + } + } else { + // return scalar values + while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { + if ($group) { + $results[$row[0]][] = $row[1]; + } else { + $results[$row[0]] = $row[1]; + } + } + if (DB::isError($row)) { + $results = $row; + } + } + + $res->free(); + + return $results; + } + + // }}} + // {{{ getAll() + + /** + * Fetch all the rows returned from a query + * + * @param string $query the SQL query + * @param array $params array to be used in execution of the statement. + * Quantity of array elements must match quantity + * of placeholders in query. This function does + * NOT support scalars. + * @param int $fetchmode the fetch mode to use + * + * @return array an nested array. DB error on failure. + * + * @access public + */ + function &getAll($query, + $params = array(), + $fetchmode = DB_FETCHMODE_DEFAULT) + { + // compat check, the params and fetchmode parameters used to + // have the opposite order + if (!is_array($params)) { + if (is_array($fetchmode)) { + if ($params === null) { + $tmp = DB_FETCHMODE_DEFAULT; + } else { + $tmp = $params; + } + $params = $fetchmode; + $fetchmode = $tmp; + } elseif ($params !== null) { + $fetchmode = $params; + $params = array(); + } + } + + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + + if (DB::isError($sth)) { + return $sth; + } + + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if (DB::isError($res) || $res === DB_OK) { + return $res; + } + + $results = array(); + while (DB_OK === $res->fetchInto($row, $fetchmode)) { + if ($fetchmode & DB_FETCHMODE_FLIPPED) { + foreach ($row as $key => $val) { + $results[$key][] = $val; + } + } else { + $results[] = $row; + } + } + + $res->free(); + + if (DB::isError($row)) { + $tmp =& $this->raiseError($row); + return $tmp; + } + return $results; + } + + // }}} + // {{{ autoCommit() + + /** + * enable automatic Commit + * + * @param boolean $onoff + * @return mixed DB_Error + * + * @access public + */ + function autoCommit($onoff=false) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ commit() + + /** + * starts a Commit + * + * @return mixed DB_Error + * + * @access public + */ + function commit() + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ rollback() + + /** + * starts a rollback + * + * @return mixed DB_Error + * + * @access public + */ + function rollback() + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ numRows() + + /** + * Returns the number of rows in a result object + * + * @param object DB_Result the result object to check + * + * @return mixed DB_Error or the number of rows + * + * @access public + */ + function numRows($result) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ affectedRows() + + /** + * Returns the affected rows of a query + * + * @return mixed DB_Error or number of rows + * + * @access public + */ + function affectedRows() + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ errorNative() + + /** + * Returns an errormessage, provides by the database + * + * @return mixed DB_Error or message + * + * @access public + */ + function errorNative() + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ getSequenceName() + + /** + * Generate the name used inside the database for a sequence + * + * The createSequence() docblock contains notes about storing sequence + * names. + * + * @param string $sqn the sequence's public name + * + * @return string the sequence's name in the backend + * + * @see DB_common::createSequence(), DB_common::dropSequence(), + * DB_common::nextID(), DB_common::setOption() + * @access private + */ + function getSequenceName($sqn) + { + return sprintf($this->getOption('seqname_format'), + preg_replace('/[^a-z0-9_.]/i', '_', $sqn)); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @see DB_common::createSequence(), DB_common::dropSequence(), + * DB_common::getSequenceName() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * The name of a given sequence is determined by passing the string + * provided in the $seq_name argument through PHP's sprintf() + * function using the value from the seqname_format option as + * the sprintf()'s format argument. + * + * seqname_format is set via setOption(). + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object is returned if + * problems arise. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_common::nextID() + * @access public + */ + function createSequence($seq_name) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. DB_Error if problems. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_common::nextID() + * @access public + */ + function dropSequence($seq_name) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * The format of the resulting array depends on which $mode + * you select. The sample output below is based on this query: + *
+     *    SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
+     *    FROM tblFoo
+     *    JOIN tblBar ON tblFoo.fldId = tblBar.fldId
+     * 
+ * + *
    + *
  • + * + * null (default) + *
    +     *   [0] => Array (
    +     *       [table] => tblFoo
    +     *       [name] => fldId
    +     *       [type] => int
    +     *       [len] => 11
    +     *       [flags] => primary_key not_null
    +     *   )
    +     *   [1] => Array (
    +     *       [table] => tblFoo
    +     *       [name] => fldPhone
    +     *       [type] => string
    +     *       [len] => 20
    +     *       [flags] =>
    +     *   )
    +     *   [2] => Array (
    +     *       [table] => tblBar
    +     *       [name] => fldId
    +     *       [type] => int
    +     *       [len] => 11
    +     *       [flags] => primary_key not_null
    +     *   )
    +     *   
    + * + *
  • + * + * DB_TABLEINFO_ORDER + * + *

    In addition to the information found in the default output, + * a notation of the number of columns is provided by the + * num_fields element while the order + * element provides an array with the column names as the keys and + * their location index number (corresponding to the keys in the + * the default output) as the values.

    + * + *

    If a result set has identical field names, the last one is + * used.

    + * + *
    +     *   [num_fields] => 3
    +     *   [order] => Array (
    +     *       [fldId] => 2
    +     *       [fldTrans] => 1
    +     *   )
    +     *   
    + * + *
  • + * + * DB_TABLEINFO_ORDERTABLE + * + *

    Similar to DB_TABLEINFO_ORDER but adds more + * dimensions to the array in which the table names are keys and + * the field names are sub-keys. This is helpful for queries that + * join tables which have identical field names.

    + * + *
    +     *   [num_fields] => 3
    +     *   [ordertable] => Array (
    +     *       [tblFoo] => Array (
    +     *           [fldId] => 0
    +     *           [fldPhone] => 1
    +     *       )
    +     *       [tblBar] => Array (
    +     *           [fldId] => 2
    +     *       )
    +     *   )
    +     *   
    + * + *
  • + *
+ * + * The flags element contains a space separated list + * of extra information about the field. This data is inconsistent + * between DBMS's due to the way each DBMS works. + * + primary_key + * + unique_key + * + multiple_key + * + not_null + * + * Most DBMS's only provide the table and flags + * elements if $result is a table name. The following DBMS's + * provide full information from queries: + * + fbsql + * + mysql + * + * If the 'portability' option has DB_PORTABILITY_LOWERCASE + * turned on, the names of tables and fields will be lowercased. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode either unused or one of the tableInfo modes: + * DB_TABLEINFO_ORDERTABLE, + * DB_TABLEINFO_ORDER or + * DB_TABLEINFO_FULL (which does both). + * These are bitwise, so the first two can be + * combined using |. + * @return array an associative array with the information requested. + * If something goes wrong an error object is returned. + * + * @see DB_common::setOption() + * @access public + */ + function tableInfo($result, $mode = null) + { + /* + * If the DB_ class has a tableInfo() method, that one + * overrides this one. But, if the driver doesn't have one, + * this method runs and tells users about that fact. + */ + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ getTables() + + /** + * @deprecated Deprecated in release 1.2 or lower + */ + function getTables() + { + return $this->getListOf('tables'); + } + + // }}} + // {{{ getListOf() + + /** + * list internal DB info + * valid values for $type are db dependent, + * often: databases, users, view, functions + * + * @param string $type type of requested info + * + * @return mixed DB_Error or the requested data + * + * @access public + */ + function getListOf($type) + { + $sql = $this->getSpecialQuery($type); + if ($sql === null) { // No support + return $this->raiseError(DB_ERROR_UNSUPPORTED); + } elseif (is_int($sql) || DB::isError($sql)) { // Previous error + return $this->raiseError($sql); + } elseif (is_array($sql)) { // Already the result + return $sql; + } + return $this->getCol($sql); // Launch this query + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Returns the query needed to get some backend info + * + * @param string $type What kind of info you want to retrieve + * + * @return string The SQL query string + * + * @access public + */ + function getSpecialQuery($type) + { + return $this->raiseError(DB_ERROR_UNSUPPORTED); + } + + // }}} + // {{{ _rtrimArrayValues() + + /** + * Right trim all strings in an array + * + * @param array $array the array to be trimmed (passed by reference) + * @return void + * @access private + */ + function _rtrimArrayValues(&$array) + { + foreach ($array as $key => $value) { + if (is_string($value)) { + $array[$key] = rtrim($value); + } + } + } + + // }}} + // {{{ _convertNullArrayValuesToEmpty() + + /** + * Convert all null values in an array to empty strings + * + * @param array $array the array to be de-nullified (passed by reference) + * @return void + * @access private + */ + function _convertNullArrayValuesToEmpty(&$array) + { + foreach ($array as $key => $value) { + if (is_null($value)) { + $array[$key] = ''; + } + } + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/dbase.php b/thirdparty/pear/DB/dbase.php new file mode 100644 index 0000000..a5ad09f --- /dev/null +++ b/thirdparty/pear/DB/dbase.php @@ -0,0 +1,225 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +// XXX legend: +// You have to compile your PHP with the --enable-dbase option + + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's dbase + * extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Stig Bakken + */ +class DB_dbase extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); + var $res_row = array(); + var $result = 0; + + // }}} + // {{{ constructor + + /** + * DB_mysql constructor. + * + * @access public + */ + function DB_dbase() + { + $this->DB_common(); + $this->phptype = 'dbase'; + $this->dbsyntax = 'dbase'; + $this->features = array( + 'prepare' => false, + 'pconnect' => false, + 'transactions' => false, + 'limit' => false + ); + $this->errorcode_map = array(); + } + + // }}} + // {{{ connect() + + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('dbase')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + $this->dsn = $dsninfo; + + $ini = ini_get('track_errors'); + if ($ini) { + $conn = @dbase_open($dsninfo['database'], 0); + } else { + ini_set('track_errors', 1); + $conn = @dbase_open($dsninfo['database'], 0); + ini_set('track_errors', $ini); + } + if (!$conn) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, + null, null, strip_tags($php_errormsg)); + } + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + function disconnect() + { + $ret = @dbase_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ &query() + + function &query($query = null) + { + // emulate result resources + $this->res_row[(int)$this->result] = 0; + $tmp =& new DB_result($this, $this->result++); + return $tmp; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum === null) { + $rownum = $this->res_row[(int)$result]++; + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @dbase_get_record_with_names($this->connection, $rownum); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @dbase_get_record($this->connection, $rownum); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ numCols() + + function numCols($foo) + { + return @dbase_numfields($this->connection); + } + + // }}} + // {{{ numRows() + + function numRows($foo) + { + return @dbase_numrecords($this->connection); + } + + // }}} + // {{{ quoteSmart() + + /** + * Format input so it can be safely used in a query + * + * @param mixed $in data to be quoted + * + * @return mixed Submitted variable's type = returned value: + * + null = the string NULL + * + boolean = T if true or + * F if false. Use the Logical + * data type. + * + integer or double = the unquoted number + * + other (including strings and numeric strings) = + * the data with single quotes escaped by preceeding + * single quotes then the whole string is encapsulated + * between single quotes + * + * @internal + */ + function quoteSmart($in) + { + if (is_int($in) || is_double($in)) { + return $in; + } elseif (is_bool($in)) { + return $in ? 'T' : 'F'; + } elseif (is_null($in)) { + return 'NULL'; + } else { + return "'" . $this->escapeSimple($in) . "'"; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/fbsql.php b/thirdparty/pear/DB/fbsql.php new file mode 100644 index 0000000..398fff6 --- /dev/null +++ b/thirdparty/pear/DB/fbsql.php @@ -0,0 +1,655 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +// XXX legend: +// +// XXX ERRORMSG: The error message from the fbsql function should +// be registered here. +// +// TODO/wishlist: +// longReadlen +// binmode + + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's FrontBase + * extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Frank M. Kromann + */ +class DB_fbsql extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); + var $num_rows = array(); + var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */ + + // }}} + // {{{ constructor + + /** + * DB_fbsql constructor. + * + * @access public + */ + function DB_fbsql() + { + $this->DB_common(); + $this->phptype = 'fbsql'; + $this->dbsyntax = 'fbsql'; + $this->features = array( + 'prepare' => false, + 'pconnect' => true, + 'transactions' => true, + 'limit' => 'emulate' + ); + $this->errorcode_map = array( + 1004 => DB_ERROR_CANNOT_CREATE, + 1005 => DB_ERROR_CANNOT_CREATE, + 1006 => DB_ERROR_CANNOT_CREATE, + 1007 => DB_ERROR_ALREADY_EXISTS, + 1008 => DB_ERROR_CANNOT_DROP, + 1046 => DB_ERROR_NODBSELECTED, + 1050 => DB_ERROR_ALREADY_EXISTS, + 1051 => DB_ERROR_NOSUCHTABLE, + 1054 => DB_ERROR_NOSUCHFIELD, + 1062 => DB_ERROR_ALREADY_EXISTS, + 1064 => DB_ERROR_SYNTAX, + 1100 => DB_ERROR_NOT_LOCKED, + 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, + 1146 => DB_ERROR_NOSUCHTABLE, + ); + } + + // }}} + // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param $persistent (optional) whether the connection should + * be persistent + * @access public + * @return int DB_OK on success, a DB error on failure + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('fbsql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsninfo; + $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; + + $php_errormsg = ''; + $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect'; + + if ($dbhost && $dsninfo['username'] && $dsninfo['password']) { + $conn = @$connect_function($dbhost, $dsninfo['username'], + $dsninfo['password']); + } elseif ($dbhost && $dsninfo['username']) { + $conn = @$connect_function($dbhost, $dsninfo['username']); + } elseif ($dbhost) { + $conn = @$connect_function($dbhost); + } else { + $conn = false; + } + if (!$conn) { + if (empty($php_errormsg)) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, + null, $php_errormsg); + } + } + + if ($dsninfo['database']) { + if (!fbsql_select_db($dsninfo['database'], $conn)) { + return $this->fbsqlRaiseError(); + } + } + + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Log out and disconnect from the database. + * + * @access public + * + * @return bool true on success, false if not connected. + */ + function disconnect() + { + $ret = @fbsql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Send a query to fbsql and return the results as a fbsql resource + * identifier. + * + * @param the SQL query + * + * @access public + * + * @return mixed returns a valid fbsql result for successful SELECT + * queries, DB_OK for other successful queries. A DB error is + * returned on failure. + */ + function simpleQuery($query) + { + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @fbsql_query("$query;", $this->connection); + if (!$result) { + return $this->fbsqlRaiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + if (DB::isManip($query)) { + return DB_OK; + } + $numrows = $this->numrows($result); + if (is_object($numrows)) { + return $numrows; + } + $this->num_rows[(int)$result] = $numrows; + return $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal fbsql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return @fbsql_next_result($result); + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + if (!@fbsql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @fbsql_fetch_array($result, FBSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @fbsql_fetch_row($result); + } + if (!$arr) { + $errno = @fbsql_errno($this->connection); + if (!$errno) { + return null; + } + return $this->fbsqlRaiseError($errno); + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Free the internal resources associated with $result. + * + * @param $result fbsql result identifier + * + * @access public + * + * @return bool true on success, false if $result is invalid + */ + function freeResult($result) + { + return @fbsql_free_result($result); + } + + // }}} + // {{{ autoCommit() + + function autoCommit($onoff=false) + { + if ($onoff) { + $this->query("SET COMMIT TRUE"); + } else { + $this->query("SET COMMIT FALSE"); + } + } + + // }}} + // {{{ commit() + + function commit() + { + @fbsql_commit(); + } + + // }}} + // {{{ rollback() + + function rollback() + { + @fbsql_rollback(); + } + + // }}} + // {{{ numCols() + + /** + * Get the number of columns in a result set. + * + * @param $result fbsql result identifier + * + * @access public + * + * @return int the number of columns per row in $result + */ + function numCols($result) + { + $cols = @fbsql_num_fields($result); + + if (!$cols) { + return $this->fbsqlRaiseError(); + } + + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Get the number of rows in a result set. + * + * @param $result fbsql result identifier + * + * @access public + * + * @return int the number of rows in $result + */ + function numRows($result) + { + $rows = @fbsql_num_rows($result); + if ($rows === null) { + return $this->fbsqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affectedRows() + + /** + * Gets the number of rows affected by the data manipulation + * query. For other queries, this function returns 0. + * + * @return number of rows affected by the last query + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + $result = @fbsql_affected_rows($this->connection); + } else { + $result = 0; + } + return $result; + } + + // }}} + // {{{ errorNative() + + /** + * Get the native error code of the last error (if any) that + * occured on the current connection. + * + * @access public + * + * @return int native fbsql error code + */ + function errorNative() + { + return @fbsql_errno($this->connection); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = 0; + do { + $result = $this->query("INSERT INTO ${seqname} (id) VALUES (NULL)"); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $result; + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $result; + } + return @fbsql_insert_id($this->connection); + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object is returned if + * problems arise. + * + * @internal + * @see DB_common::createSequence() + * @access public + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("CREATE TABLE ${seqname} ". + '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'. + ' PRIMARY KEY(id))'); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. DB_Error if problems. + * + * @internal + * @see DB_common::dropSequence() + * @access public + */ + function dropSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("DROP TABLE ${seqname} RESTRICT"); + } + + // }}} + // {{{ modifyQuery() + + function modifyQuery($query) + { + if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { + // "DELETE FROM table" gives 0 affected rows in fbsql. + // This little hack lets you know how many rows were deleted. + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { + $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', + 'DELETE FROM \1 WHERE 1=1', $query); + } + } + return $query; + } + + // }}} + // {{{ quoteSmart() + + /** + * Format input so it can be safely used in a query + * + * @param mixed $in data to be quoted + * + * @return mixed Submitted variable's type = returned value: + * + null = the string NULL + * + boolean = string TRUE or FALSE + * + integer or double = the unquoted number + * + other (including strings and numeric strings) = + * the data escaped according to MySQL's settings + * then encapsulated between single quotes + * + * @internal + */ + function quoteSmart($in) + { + if (is_int($in) || is_double($in)) { + return $in; + } elseif (is_bool($in)) { + return $in ? 'TRUE' : 'FALSE'; + } elseif (is_null($in)) { + return 'NULL'; + } else { + return "'" . $this->escapeSimple($in) . "'"; + } + } + + // }}} + // {{{ fbsqlRaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $errno PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see DB_common::errorCode() + * @see DB_common::raiseError() + */ + function fbsqlRaiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode(fbsql_errno($this->connection)); + } + return $this->raiseError($errno, null, null, null, + @fbsql_error($this->connection)); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table + * @param int $mode a valid tableInfo mode + * @return array an associative array with the information requested + * or an error object if something is wrong + * @access public + * @internal + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } elseif (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @fbsql_list_fields($this->dsn['database'], + $result, $this->connection); + $got_string = true; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @fbsql_num_fields($id); + + // made this IF due to performance (one if is faster than $count if's) + if (!$mode) { + for ($i=0; $i<$count; $i++) { + $res[$i]['table'] = $case_func(@fbsql_field_table($id, $i)); + $res[$i]['name'] = $case_func(@fbsql_field_name($id, $i)); + $res[$i]['type'] = @fbsql_field_type($id, $i); + $res[$i]['len'] = @fbsql_field_len($id, $i); + $res[$i]['flags'] = @fbsql_field_flags($id, $i); + } + } else { // full + $res["num_fields"]= $count; + + for ($i=0; $i<$count; $i++) { + $res[$i]['table'] = $case_func(@fbsql_field_table($id, $i)); + $res[$i]['name'] = $case_func(@fbsql_field_name($id, $i)); + $res[$i]['type'] = @fbsql_field_type($id, $i); + $res[$i]['len'] = @fbsql_field_len($id, $i); + $res[$i]['flags'] = @fbsql_field_flags($id, $i); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + } + + // free the result only if we were called on a table + if ($got_string) { + @fbsql_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Returns the query needed to get some backend info + * @param string $type What kind of info you want to retrieve + * @return string The SQL query string + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'select "table_name" from information_schema.tables'; + default: + return null; + } + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/ibase.php b/thirdparty/pear/DB/ibase.php new file mode 100644 index 0000000..e2eddb0 --- /dev/null +++ b/thirdparty/pear/DB/ibase.php @@ -0,0 +1,784 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +// Bugs: +// - If dbsyntax is not firebird, the limitQuery may fail + + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's Interbase + * extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Sterling Hughes + */ +class DB_ibase extends DB_common +{ + + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $autocommit = 1; + var $manip_query = array(); + + // }}} + // {{{ constructor + + function DB_ibase() + { + $this->DB_common(); + $this->phptype = 'ibase'; + $this->dbsyntax = 'ibase'; + $this->features = array( + 'prepare' => true, + 'pconnect' => true, + 'transactions' => true, + 'limit' => false + ); + // just a few of the tons of Interbase error codes listed in the + // Language Reference section of the Interbase manual + $this->errorcode_map = array( + -104 => DB_ERROR_SYNTAX, + -150 => DB_ERROR_ACCESS_VIOLATION, + -151 => DB_ERROR_ACCESS_VIOLATION, + -155 => DB_ERROR_NOSUCHTABLE, + 88 => DB_ERROR_NOSUCHTABLE, + -157 => DB_ERROR_NOSUCHFIELD, + -158 => DB_ERROR_VALUE_COUNT_ON_ROW, + -170 => DB_ERROR_MISMATCH, + -171 => DB_ERROR_MISMATCH, + -172 => DB_ERROR_INVALID, + -204 => DB_ERROR_INVALID, + -205 => DB_ERROR_NOSUCHFIELD, + -206 => DB_ERROR_NOSUCHFIELD, + -208 => DB_ERROR_INVALID, + -219 => DB_ERROR_NOSUCHTABLE, + -297 => DB_ERROR_CONSTRAINT, + -530 => DB_ERROR_CONSTRAINT, + -607 => DB_ERROR_NOSUCHTABLE, + -803 => DB_ERROR_CONSTRAINT, + -551 => DB_ERROR_ACCESS_VIOLATION, + -552 => DB_ERROR_ACCESS_VIOLATION, + -922 => DB_ERROR_NOSUCHDB, + -923 => DB_ERROR_CONNECT_FAILED, + -924 => DB_ERROR_CONNECT_FAILED + ); + } + + // }}} + // {{{ connect() + + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('interbase')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + $this->dsn = $dsninfo; + $dbhost = $dsninfo['hostspec'] ? + ($dsninfo['hostspec'] . ':' . $dsninfo['database']) : + $dsninfo['database']; + + $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect'; + + $params = array(); + $params[] = $dbhost; + $params[] = $dsninfo['username'] ? $dsninfo['username'] : null; + $params[] = $dsninfo['password'] ? $dsninfo['password'] : null; + $params[] = isset($dsninfo['charset']) ? $dsninfo['charset'] : null; + $params[] = isset($dsninfo['buffers']) ? $dsninfo['buffers'] : null; + $params[] = isset($dsninfo['dialect']) ? $dsninfo['dialect'] : null; + $params[] = isset($dsninfo['role']) ? $dsninfo['role'] : null; + + $conn = @call_user_func_array($connect_function, $params); + if (!$conn) { + return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED); + } + $this->connection = $conn; + if ($this->dsn['dbsyntax'] == 'firebird') { + $this->features['limit'] = 'alter'; + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + function disconnect() + { + $ret = @ibase_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @ibase_query($this->connection, $query); + if (!$result) { + return $this->ibaseRaiseError(); + } + if ($this->autocommit && $ismanip) { + @ibase_commit($this->connection); + } + // Determine which queries that should return data, and which + // should return an error code only. + return $ismanip ? DB_OK : $result; + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * This method is used by backends to alter limited queries + * Uses the new FIRST n SKIP n Firebird 1.0 syntax, so it is + * only compatible with Firebird 1.x + * + * @param string $query query to modify + * @param integer $from the row to start to fetching + * @param integer $count the numbers of rows to fetch + * + * @return the new (modified) query + * @author Ludovico Magnocavallo + * @access private + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + if ($this->dsn['dbsyntax'] == 'firebird') { + //$from++; // SKIP starts from 1, ie SKIP 1 starts from the first record + // (cox) Seems that SKIP starts in 0 + $query = preg_replace('/^\s*select\s(.*)$/is', + "SELECT FIRST $count SKIP $from $1", $query); + } + return $query; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal ibase result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + if (function_exists('ibase_fetch_assoc')) { + $arr = @ibase_fetch_assoc($result); + } else { + $arr = get_object_vars(ibase_fetch_object($result)); + } + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @ibase_fetch_row($result); + } + if (!$arr) { + if ($errmsg = @ibase_errmsg()) { + return $this->ibaseRaiseError(null, $errmsg); + } else { + return null; + } + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + function freeResult($result) + { + return @ibase_free_result($result); + } + + // }}} + // {{{ freeQuery() + + function freeQuery($query) + { + @ibase_free_query($query); + return true; + } + + // }}} + // {{{ numCols() + + function numCols($result) + { + $cols = @ibase_num_fields($result); + if (!$cols) { + return $this->ibaseRaiseError(); + } + return $cols; + } + + // }}} + // {{{ prepare() + + /** + * Prepares a query for multiple execution with execute(). + * + * prepare() requires a generic query as string like + * INSERT INTO numbers VALUES (?, ?, ?) + * . The ? characters are placeholders. + * + * Three types of placeholders can be used: + * + ? a quoted scalar value, i.e. strings, integers + * + ! value is inserted 'as is' + * + & requires a file name. The file's contents get + * inserted into the query (i.e. saving binary + * data in a db) + * + * Use backslashes to escape placeholder characters if you don't want + * them to be interpreted as placeholders. Example: + * "UPDATE foo SET col=? WHERE col='over \& under'" + * + * + * @param string $query query to be prepared + * @return mixed DB statement resource on success. DB_Error on failure. + */ + function prepare($query) + { + $tokens = preg_split('/((? $val) { + switch ($val) { + case '?': + $types[$token++] = DB_PARAM_SCALAR; + break; + case '&': + $types[$token++] = DB_PARAM_OPAQUE; + break; + case '!': + $types[$token++] = DB_PARAM_MISC; + break; + default: + $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); + $newquery .= $tokens[$key] . '?'; + } + } + + $newquery = substr($newquery, 0, -1); + $this->last_query = $query; + $newquery = $this->modifyQuery($newquery); + $stmt = @ibase_prepare($this->connection, $newquery); + $this->prepare_types[(int)$stmt] = $types; + $this->manip_query[(int)$stmt] = DB::isManip($query); + return $stmt; + } + + // }}} + // {{{ execute() + + /** + * Executes a DB statement prepared with prepare(). + * + * @param resource $stmt a DB statement resource returned from prepare() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 for non-array items or the + * quantity of elements in the array. + * @return object a new DB_Result or a DB_Error when fail + * @see DB_ibase::prepare() + * @access public + */ + function &execute($stmt, $data = array()) + { + if (!is_array($data)) { + $data = array($data); + } + + $types =& $this->prepare_types[(int)$stmt]; + if (count($types) != count($data)) { + $tmp =& $this->raiseError(DB_ERROR_MISMATCH); + return $tmp; + } + + $i = 0; + foreach ($data as $key => $value) { + if ($types[$i] == DB_PARAM_MISC) { + /* + * ibase doesn't seem to have the ability to pass a + * parameter along unchanged, so strip off quotes from start + * and end, plus turn two single quotes to one single quote, + * in order to avoid the quotes getting escaped by + * ibase and ending up in the database. + */ + $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); + $data[$key] = str_replace("''", "'", $data[$key]); + } elseif ($types[$i] == DB_PARAM_OPAQUE) { + $fp = @fopen($data[$key], 'rb'); + if (!$fp) { + $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + return $tmp; + } + $data[$key] = fread($fp, filesize($data[$key])); + fclose($fp); + } + $i++; + } + + array_unshift($data, $stmt); + + $res = call_user_func_array('ibase_execute', $data); + if (!$res) { + $tmp =& $this->ibaseRaiseError(); + return $tmp; + } + /* XXX need this? + if ($this->autocommit && $this->manip_query[(int)$stmt]) { + @ibase_commit($this->connection); + }*/ + if ($this->manip_query[(int)$stmt]) { + $tmp = DB_OK; + } else { + $tmp =& new DB_result($this, $res); + } + return $tmp; + } + + /** + * Free the internal resources associated with a prepared query. + * + * @param $stmt The interbase_query resource type + * + * @return bool true on success, false if $result is invalid + */ + function freePrepared($stmt) + { + if (!is_resource($stmt)) { + return false; + } + @ibase_free_query($stmt); + unset($this->prepare_tokens[(int)$stmt]); + unset($this->prepare_types[(int)$stmt]); + unset($this->manip_query[(int)$stmt]); + return true; + } + + // }}} + // {{{ autoCommit() + + function autoCommit($onoff = false) + { + $this->autocommit = $onoff ? 1 : 0; + return DB_OK; + } + + // }}} + // {{{ commit() + + function commit() + { + return @ibase_commit($this->connection); + } + + // }}} + // {{{ rollback() + + function rollback() + { + return @ibase_rollback($this->connection); + } + + // }}} + // {{{ transactionInit() + + function transactionInit($trans_args = 0) + { + return $trans_args ? @ibase_trans($trans_args, $this->connection) : @ibase_trans(); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $sqn = strtoupper($this->getSequenceName($seq_name)); + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result =& $this->query("SELECT GEN_ID(${sqn}, 1) " + . 'FROM RDB$GENERATORS ' + . "WHERE RDB\$GENERATOR_NAME='${sqn}'"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result)) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $result; + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + $result->free(); + return $arr[0]; + } + + // }}} + // {{{ createSequence() + + /** + * Create the sequence + * + * @param string $seq_name the name of the sequence + * @return mixed DB_OK on success or DB error on error + * @access public + */ + function createSequence($seq_name) + { + $sqn = strtoupper($this->getSequenceName($seq_name)); + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("CREATE GENERATOR ${sqn}"); + $this->popErrorHandling(); + + return $result; + } + + // }}} + // {{{ dropSequence() + + /** + * Drop a sequence + * + * @param string $seq_name the name of the sequence + * @return mixed DB_OK on success or DB error on error + * @access public + */ + function dropSequence($seq_name) + { + $sqn = strtoupper($this->getSequenceName($seq_name)); + return $this->query('DELETE FROM RDB$GENERATORS ' + . "WHERE RDB\$GENERATOR_NAME='${sqn}'"); + } + + // }}} + // {{{ _ibaseFieldFlags() + + /** + * get the Flags of a Field + * + * @param string $field_name the name of the field + * @param string $table_name the name of the table + * + * @return string The flags of the field ("primary_key", "unique_key", "not_null" + * "default", "computed" and "blob" are supported) + * @access private + */ + function _ibaseFieldFlags($field_name, $table_name) + { + $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE' + .' FROM RDB$INDEX_SEGMENTS I' + .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME' + .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\'' + .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''; + + $result = @ibase_query($this->connection, $sql); + if (!$result) { + return $this->ibaseRaiseError(); + } + + $flags = ''; + if ($obj = @ibase_fetch_object($result)) { + @ibase_free_result($result); + if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') { + $flags .= 'primary_key '; + } + if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') { + $flags .= 'unique_key '; + } + } + + $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,' + .' R.RDB$DEFAULT_SOURCE AS DSOURCE,' + .' F.RDB$FIELD_TYPE AS FTYPE,' + .' F.RDB$COMPUTED_SOURCE AS CSOURCE' + .' FROM RDB$RELATION_FIELDS R ' + .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME' + .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'' + .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\''; + + $result = @ibase_query($this->connection, $sql); + if (!$result) { + return $this->ibaseRaiseError(); + } + if ($obj = @ibase_fetch_object($result)) { + @ibase_free_result($result); + if (isset($obj->NFLAG)) { + $flags .= 'not_null '; + } + if (isset($obj->DSOURCE)) { + $flags .= 'default '; + } + if (isset($obj->CSOURCE)) { + $flags .= 'computed '; + } + if (isset($obj->FTYPE) && $obj->FTYPE == 261) { + $flags .= 'blob '; + } + } + + return trim($flags); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set. + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table + * @param int $mode a valid tableInfo mode + * @return array an associative array with the information requested + * or an error object if something is wrong + * @access public + * @internal + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } elseif (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @ibase_query($this->connection, + "SELECT * FROM $result WHERE 1=0"); + $got_string = true; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @ibase_num_fields($id); + + // made this IF due to performance (one if is faster than $count if's) + if (!$mode) { + for ($i=0; $i<$count; $i++) { + $info = @ibase_field_info($id, $i); + $res[$i]['table'] = $got_string ? $case_func($result) : ''; + $res[$i]['name'] = $case_func($info['name']); + $res[$i]['type'] = $info['type']; + $res[$i]['len'] = $info['length']; + $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : ''; + } + } else { // full + $res['num_fields']= $count; + + for ($i=0; $i<$count; $i++) { + $info = @ibase_field_info($id, $i); + $res[$i]['table'] = $got_string ? $case_func($result) : ''; + $res[$i]['name'] = $case_func($info['name']); + $res[$i]['type'] = $info['type']; + $res[$i]['len'] = $info['length']; + $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : ''; + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + } + + // free the result only if we were called on a table + if ($got_string) { + @ibase_free_result($id); + } + return $res; + } + + // }}} + // {{{ ibaseRaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $db_errno PEAR error number (usually a DB constant) if + * manually raising an error + * @param string $native_errmsg text of error message if known + * @return object DB error object + * @see DB_common::errorCode() + * @see DB_common::raiseError() + */ + function &ibaseRaiseError($db_errno = null, $native_errmsg = null) + { + if ($native_errmsg === null) { + $native_errmsg = @ibase_errmsg(); + } + // memo for the interbase php module hackers: we need something similar + // to mysql_errno() to retrieve error codes instead of this ugly hack + if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/', $native_errmsg, $m)) { + $native_errno = (int)$m[2]; + } else { + $native_errno = null; + } + // try to map the native error to the DB one + if ($db_errno === null) { + if ($native_errno) { + // try to interpret Interbase error code (that's why we need ibase_errno() + // in the interbase module to return the real error code) + switch ($native_errno) { + case -204: + if (is_int(strpos($m[3], 'Table unknown'))) { + $db_errno = DB_ERROR_NOSUCHTABLE; + } + break; + default: + $db_errno = $this->errorCode($native_errno); + } + } else { + $error_regexps = array( + '/[tT]able not found/' => DB_ERROR_NOSUCHTABLE, + '/[tT]able .* already exists/' => DB_ERROR_ALREADY_EXISTS, + '/validation error for column .* value "\*\*\* null/' => DB_ERROR_CONSTRAINT_NOT_NULL, + '/violation of [\w ]+ constraint/' => DB_ERROR_CONSTRAINT, + '/conversion error from string/' => DB_ERROR_INVALID_NUMBER, + '/no permission for/' => DB_ERROR_ACCESS_VIOLATION, + '/arithmetic exception, numeric overflow, or string truncation/' => DB_ERROR_DIVZERO + ); + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $native_errmsg)) { + $db_errno = $code; + $native_errno = null; + break; + } + } + } + } + $tmp =& $this->raiseError($db_errno, null, null, null, $native_errmsg); + return $tmp; + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/ifx.php b/thirdparty/pear/DB/ifx.php new file mode 100644 index 0000000..d317a3a --- /dev/null +++ b/thirdparty/pear/DB/ifx.php @@ -0,0 +1,579 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +// Legend: +// For more info on Informix errors see: +// http://www.informix.com/answers/english/ierrors.htm +// +// TODO: +// - set needed env Informix vars on connect +// - implement native prepare/execute + + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's Informix + * extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Tomas V.V.Cox + */ +class DB_ifx extends DB_common +{ + // {{{ properties + + var $connection; + var $affected = 0; + var $dsn = array(); + var $transaction_opcount = 0; + var $autocommit = true; + var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */ + + // }}} + // {{{ constructor + + function DB_ifx() + { + $this->phptype = 'ifx'; + $this->dbsyntax = 'ifx'; + $this->features = array( + 'prepare' => false, + 'pconnect' => true, + 'transactions' => true, + 'limit' => 'emulate' + ); + $this->errorcode_map = array( + '-201' => DB_ERROR_SYNTAX, + '-206' => DB_ERROR_NOSUCHTABLE, + '-217' => DB_ERROR_NOSUCHFIELD, + '-239' => DB_ERROR_CONSTRAINT, + '-253' => DB_ERROR_SYNTAX, + '-292' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-310' => DB_ERROR_ALREADY_EXISTS, + '-329' => DB_ERROR_NODBSELECTED, + '-346' => DB_ERROR_CONSTRAINT, + '-386' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-391' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-554' => DB_ERROR_SYNTAX, + '-691' => DB_ERROR_CONSTRAINT, + '-703' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-1204' => DB_ERROR_INVALID_DATE, + '-1205' => DB_ERROR_INVALID_DATE, + '-1206' => DB_ERROR_INVALID_DATE, + '-1209' => DB_ERROR_INVALID_DATE, + '-1210' => DB_ERROR_INVALID_DATE, + '-1212' => DB_ERROR_INVALID_DATE, + '-1213' => DB_ERROR_INVALID_NUMBER, + ); + } + + // }}} + // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param $persistent (optional) whether the connection should + * be persistent + * + * @return int DB_OK on success, a DB error code on failure + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('informix') && + !DB::assertExtension('Informix')) + { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + $this->dsn = $dsninfo; + $dbhost = $dsninfo['hostspec'] ? '@' . $dsninfo['hostspec'] : ''; + $dbname = $dsninfo['database'] ? $dsninfo['database'] . $dbhost : ''; + $user = $dsninfo['username'] ? $dsninfo['username'] : ''; + $pw = $dsninfo['password'] ? $dsninfo['password'] : ''; + + $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect'; + + $this->connection = @$connect_function($dbname, $user, $pw); + if (!is_resource($this->connection)) { + return $this->ifxraiseError(DB_ERROR_CONNECT_FAILED); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Log out and disconnect from the database. + * + * @return bool true on success, false if not connected. + */ + function disconnect() + { + $ret = @ifx_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Send a query to Informix and return the results as a + * Informix resource identifier. + * + * @param $query the SQL query + * + * @return int returns a valid Informix result for successful SELECT + * queries, DB_OK for other successful queries. A DB error code + * is returned on failure. + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $this->affected = null; + if (preg_match('/(SELECT)/i', $query)) { //TESTME: Use !DB::isManip()? + // the scroll is needed for fetching absolute row numbers + // in a select query result + $result = @ifx_query($query, $this->connection, IFX_SCROLL); + } else { + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @ifx_query('BEGIN WORK', $this->connection); + if (!$result) { + return $this->ifxraiseError(); + } + } + $this->transaction_opcount++; + } + $result = @ifx_query($query, $this->connection); + } + if (!$result) { + return $this->ifxraiseError(); + } + $this->affected = @ifx_affected_rows($result); + // Determine which queries should return data, and which + // should return an error code only. + if (preg_match('/(SELECT)/i', $query)) { + return $result; + } + // XXX Testme: free results inside a transaction + // may cause to stop it and commit the work? + + // Result has to be freed even with a insert or update + @ifx_free_result($result); + + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal ifx result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ affectedRows() + + /** + * Gets the number of rows affected by the last query. + * if the last query was a select, returns 0. + * + * @return number of rows affected by the last query + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + return $this->affected; + } else { + return 0; + } + + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if (($rownum !== null) && ($rownum < 0)) { + return null; + } + if ($rownum === null) { + /* + * Even though fetch_row() should return the next row if + * $rownum is null, it doesn't in all cases. Bug 598. + */ + $rownum = 'NEXT'; + } else { + // Index starts at row 1, unlike most DBMS's starting at 0. + $rownum++; + } + if (!$arr = @ifx_fetch_row($result, $rownum)) { + return null; + } + if ($fetchmode !== DB_FETCHMODE_ASSOC) { + $i=0; + $order = array(); + foreach ($arr as $val) { + $order[$i++] = $val; + } + $arr = $order; + } elseif ($fetchmode == DB_FETCHMODE_ASSOC && + $this->options['portability'] & DB_PORTABILITY_LOWERCASE) + { + $arr = array_change_key_case($arr, CASE_LOWER); + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ numRows() + + function numRows($result) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ numCols() + + /** + * Get the number of columns in a result set. + * + * @param $result Informix result identifier + * + * @return int the number of columns per row in $result + */ + function numCols($result) + { + if (!$cols = @ifx_num_fields($result)) { + return $this->ifxraiseError(); + } + return $cols; + } + + // }}} + // {{{ freeResult() + + /** + * Free the internal resources associated with $result. + * + * @param $result Informix result identifier + * + * @return bool true on success, false if $result is invalid + */ + function freeResult($result) + { + return @ifx_free_result($result); + } + + // }}} + // {{{ autoCommit() + + /** + * Enable/disable automatic commits + */ + function autoCommit($onoff = true) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commit the current transaction. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + $result = @ifx_query('COMMIT WORK', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->ifxRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Roll back (undo) the current transaction. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + $result = @ifx_query('ROLLBACK WORK', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->ifxRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ ifxraiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $errno PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see errorNative() + * @see errorCode() + * @see DB_common::raiseError() + */ + function ifxraiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode(ifx_error()); + } + + return $this->raiseError($errno, null, null, null, + $this->errorNative()); + } + + // }}} + // {{{ errorCode() + + /** + * Map native error codes to DB's portable ones. + * + * Requires that the DB implementation's constructor fills + * in the $errorcode_map property. + * + * @param string $nativecode error code returned by the database + * @return int a portable DB error code, or DB_ERROR if this DB + * implementation has no mapping for the given error code. + */ + function errorCode($nativecode) + { + if (ereg('SQLCODE=(.*)]', $nativecode, $match)) { + $code = $match[1]; + if (isset($this->errorcode_map[$code])) { + return $this->errorcode_map[$code]; + } + } + return DB_ERROR; + } + + // }}} + // {{{ errorNative() + + /** + * Get the native error message of the last error (if any) that + * occured on the current connection. + * + * @return int native Informix error code + */ + function errorNative() + { + return @ifx_error() . ' ' . @ifx_errormsg(); + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Returns the query needed to get some backend info + * @param string $type What kind of info you want to retrieve + * @return string The SQL query string + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'select tabname from systables where tabid >= 100'; + default: + return null; + } + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set. + * + * NOTE: only supports 'table' if $result is a table name. + * + * If analyzing a query result and the result has duplicate field names, + * an error will be raised saying + * can't distinguish duplicate field names. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table + * @param int $mode a valid tableInfo mode + * @return array an associative array with the information requested + * or an error object if something is wrong + * @access public + * @internal + * @since 1.6.0 + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } elseif (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @ifx_query("SELECT * FROM $result WHERE 1=0", + $this->connection); + $got_string = true; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + $flds = @ifx_fieldproperties($id); + $count = @ifx_num_fields($id); + + if (count($flds) != $count) { + return $this->raiseError("can't distinguish duplicate field names"); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $i = 0; + // made this IF due to performance (one if is faster than $count if's) + if (!$mode) { + foreach ($flds as $key => $value) { + $props = explode(';', $value); + + $res[$i]['table'] = $got_string ? $case_func($result) : ''; + $res[$i]['name'] = $case_func($key); + $res[$i]['type'] = $props[0]; + $res[$i]['len'] = $props[1]; + $res[$i]['flags'] = $props[4] == 'N' ? 'not_null' : ''; + $i++; + } + + } else { // full + $res['num_fields'] = $count; + + foreach ($flds as $key => $value) { + $props = explode(';', $value); + + $res[$i]['table'] = $got_string ? $case_func($result) : ''; + $res[$i]['name'] = $case_func($key); + $res[$i]['type'] = $props[0]; + $res[$i]['len'] = $props[1]; + $res[$i]['flags'] = $props[4] == 'N' ? 'not_null' : ''; + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + $i++; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @ifx_free_result($id); + } + return $res; + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/msql.php b/thirdparty/pear/DB/msql.php new file mode 100644 index 0000000..c135b32 --- /dev/null +++ b/thirdparty/pear/DB/msql.php @@ -0,0 +1,242 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's Mini-SQL + * extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Sterling Hughes + */ +class DB_msql extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); + + // }}} + // {{{ constructor + + function DB_msql() + { + $this->DB_common(); + $this->phptype = 'msql'; + $this->dbsyntax = 'msql'; + $this->features = array( + 'prepare' => false, + 'pconnect' => true, + 'transactions' => false, + 'limit' => 'emulate' + ); + } + + // }}} + // {{{ connect() + + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('msql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsninfo; + $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; + + $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect'; + + if ($dbhost && $dsninfo['username'] && $dsninfo['password']) { + $conn = $connect_function($dbhost, $dsninfo['username'], + $dsninfo['password']); + } elseif ($dbhost && $dsninfo['username']) { + $conn = $connect_function($dbhost, $dsninfo['username']); + } else { + $conn = $connect_function($dbhost); + } + if (!$conn) { + $this->raiseError(DB_ERROR_CONNECT_FAILED); + } + if (!@msql_select_db($dsninfo['database'], $conn)){ + return $this->raiseError(DB_ERROR_NODBSELECTED); + } + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + function disconnect() + { + $ret = @msql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + function simpleQuery($query) + { + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @msql_query($query, $this->connection); + if (!$result) { + return $this->raiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + return DB::isManip($query) ? DB_OK : $result; + } + + + // }}} + // {{{ nextResult() + + /** + * Move the internal msql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + if (!@msql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @msql_fetch_array($result, MSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @msql_fetch_row($result); + } + if (!$arr) { + if ($error = @msql_error()) { + return $this->raiseError($error); + } else { + return null; + } + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + function freeResult($result) + { + return @msql_free_result($result); + } + + // }}} + // {{{ numCols() + + function numCols($result) + { + $cols = @msql_num_fields($result); + if (!$cols) { + return $this->raiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + function numRows($result) + { + $rows = @msql_num_rows($result); + if (!$rows) { + return $this->raiseError(); + } + return $rows; + } + + // }}} + // {{{ affected() + + /** + * Gets the number of rows affected by a query. + * + * @return number of rows affected by the last query + */ + function affectedRows() + { + return @msql_affected_rows($this->connection); + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/mssql.php b/thirdparty/pear/DB/mssql.php new file mode 100644 index 0000000..5bb843a --- /dev/null +++ b/thirdparty/pear/DB/mssql.php @@ -0,0 +1,738 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's Microsoft SQL Server + * extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Sterling Hughes + */ +class DB_mssql extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); + var $transaction_opcount = 0; + var $autocommit = true; + var $_db = null; + + // }}} + // {{{ constructor + + function DB_mssql() + { + $this->DB_common(); + $this->phptype = 'mssql'; + $this->dbsyntax = 'mssql'; + $this->features = array( + 'prepare' => false, + 'pconnect' => true, + 'transactions' => true, + 'limit' => 'emulate' + ); + // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX + $this->errorcode_map = array( + 170 => DB_ERROR_SYNTAX, + 207 => DB_ERROR_NOSUCHFIELD, + 208 => DB_ERROR_NOSUCHTABLE, + 245 => DB_ERROR_INVALID_NUMBER, + 515 => DB_ERROR_CONSTRAINT_NOT_NULL, + 547 => DB_ERROR_CONSTRAINT, + 2627 => DB_ERROR_CONSTRAINT, + 2714 => DB_ERROR_ALREADY_EXISTS, + 3701 => DB_ERROR_NOSUCHTABLE, + 8134 => DB_ERROR_DIVZERO, + ); + } + + // }}} + // {{{ connect() + + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('mssql') && !DB::assertExtension('sybase') + && !DB::assertExtension('sybase_ct')) + { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + $this->dsn = $dsninfo; + $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; + $dbhost .= $dsninfo['port'] ? ',' . $dsninfo['port'] : ''; + + $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect'; + + if ($dbhost && $dsninfo['username'] && $dsninfo['password']) { + $conn = @$connect_function($dbhost, $dsninfo['username'], + $dsninfo['password']); + } elseif ($dbhost && $dsninfo['username']) { + $conn = @$connect_function($dbhost, $dsninfo['username']); + } else { + $conn = @$connect_function($dbhost); + } + if (!$conn) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, + null, @mssql_get_last_message()); + } + if ($dsninfo['database']) { + if (!@mssql_select_db($dsninfo['database'], $conn)) { + return $this->raiseError(DB_ERROR_NODBSELECTED, null, null, + null, @mssql_get_last_message()); + } + $this->_db = $dsninfo['database']; + } + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + function disconnect() + { + $ret = @mssql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $query = $this->modifyQuery($query); + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @mssql_query('BEGIN TRAN', $this->connection); + if (!$result) { + return $this->mssqlRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @mssql_query($query, $this->connection); + if (!$result) { + return $this->mssqlRaiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + return $ismanip ? DB_OK : $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal mssql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return @mssql_next_result($result); + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + if (!@mssql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @mssql_fetch_array($result, MSSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @mssql_fetch_row($result); + } + if (!$arr) { + /* This throws informative error messages, + don't use it for now + if ($msg = @mssql_get_last_message()) { + return $this->raiseError($msg); + } + */ + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + function freeResult($result) + { + return @mssql_free_result($result); + } + + // }}} + // {{{ numCols() + + function numCols($result) + { + $cols = @mssql_num_fields($result); + if (!$cols) { + return $this->mssqlRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + function numRows($result) + { + $rows = @mssql_num_rows($result); + if ($rows === false) { + return $this->mssqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ autoCommit() + + /** + * Enable/disable automatic commits + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commit the current transaction. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @mssql_query('COMMIT TRAN', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mssqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Roll back (undo) the current transaction. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @mssql_query('ROLLBACK TRAN', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mssqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Gets the number of rows affected by the last query. + * if the last query was a select, returns 0. + * + * @return number of rows affected by the last query or DB_ERROR + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + $res = @mssql_query('select @@rowcount', $this->connection); + if (!$res) { + return $this->mssqlRaiseError(); + } + $ar = @mssql_fetch_row($res); + if (!$ar) { + $result = 0; + } else { + @mssql_free_result($res); + $result = $ar[0]; + } + } else { + $result = 0; + } + return $result; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) + { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } elseif (!DB::isError($result)) { + $result =& $this->query("SELECT @@IDENTITY FROM $seqname"); + $repeat = 0; + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $result = $result->fetchRow(DB_FETCHMODE_ORDERED); + return $result[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object is returned if + * problems arise. + * + * @internal + * @see DB_common::createSequence() + * @access public + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("CREATE TABLE $seqname ". + '([id] [int] IDENTITY (1, 1) NOT NULL ,' . + '[vapor] [int] NULL)'); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. DB_Error if problems. + * + * @internal + * @see DB_common::dropSequence() + * @access public + */ + function dropSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("DROP TABLE $seqname"); + } + + // }}} + // {{{ errorNative() + + /** + * Determine MS SQL Server error code by querying @@ERROR. + * + * @return mixed mssql's native error code or DB_ERROR if unknown. + */ + function errorNative() + { + $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection); + if (!$res) { + return DB_ERROR; + } + $row = @mssql_fetch_row($res); + return $row[0]; + } + + // }}} + // {{{ errorCode() + + /** + * Determine PEAR::DB error code from mssql's native codes. + * + * If $nativecode isn't known yet, it will be looked up. + * + * @param mixed $nativecode mssql error code, if known + * @return integer an error number from a DB error constant + * @see errorNative() + */ + function errorCode($nativecode = null) + { + if (!$nativecode) { + $nativecode = $this->errorNative(); + } + if (isset($this->errorcode_map[$nativecode])) { + return $this->errorcode_map[$nativecode]; + } else { + return DB_ERROR; + } + } + + // }}} + // {{{ mssqlRaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $code PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see errorCode() + * @see errorNative() + * @see DB_common::raiseError() + */ + function mssqlRaiseError($code = null) + { + $message = @mssql_get_last_message(); + if (!$code) { + $code = $this->errorNative(); + } + return $this->raiseError($this->errorCode($code), null, null, null, + "$code - $message"); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set. + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table + * @param int $mode a valid tableInfo mode + * @return array an associative array with the information requested + * or an error object if something is wrong + * @access public + * @internal + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } elseif (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $id = @mssql_query("SELECT * FROM $result WHERE 1=0", + $this->connection); + $got_string = true; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @mssql_num_fields($id); + + // made this IF due to performance (one if is faster than $count if's) + if (!$mode) { + for ($i=0; $i<$count; $i++) { + $res[$i]['table'] = $got_string ? $case_func($result) : ''; + $res[$i]['name'] = $case_func(@mssql_field_name($id, $i)); + $res[$i]['type'] = @mssql_field_type($id, $i); + $res[$i]['len'] = @mssql_field_length($id, $i); + // We only support flags for tables + $res[$i]['flags'] = $got_string ? $this->_mssql_field_flags($result, $res[$i]['name']) : ''; + } + + } else { // full + $res['num_fields']= $count; + + for ($i=0; $i<$count; $i++) { + $res[$i]['table'] = $got_string ? $case_func($result) : ''; + $res[$i]['name'] = $case_func(@mssql_field_name($id, $i)); + $res[$i]['type'] = @mssql_field_type($id, $i); + $res[$i]['len'] = @mssql_field_length($id, $i); + // We only support flags for tables + $res[$i]['flags'] = $got_string ? $this->_mssql_field_flags($result, $res[$i]['name']) : ''; + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + } + + // free the result only if we were called on a table + if ($got_string) { + @mssql_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Returns the query needed to get some backend info + * @param string $type What kind of info you want to retrieve + * @return string The SQL query string + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return "select name from sysobjects where type = 'U' order by name"; + case 'views': + return "select name from sysobjects where type = 'V'"; + default: + return null; + } + } + + // }}} + // {{{ _mssql_field_flags() + + /** + * Get the flags for a field, currently supports "not_null", "primary_key", + * "auto_increment" (mssql identity), "timestamp" (mssql timestamp), + * "unique_key" (mssql unique index, unique check or primary_key) and + * "multiple_key" (multikey index) + * + * mssql timestamp is NOT similar to the mysql timestamp so this is maybe + * not useful at all - is the behaviour of mysql_field_flags that primary + * keys are alway unique? is the interpretation of multiple_key correct? + * + * @param string The table name + * @param string The field + * @author Joern Barthel + * @access private + */ + function _mssql_field_flags($table, $column) + { + static $tableName = null; + static $flags = array(); + + if ($table != $tableName) { + + $flags = array(); + $tableName = $table; + + // get unique and primary keys + $res = $this->getAll("EXEC SP_HELPINDEX[$table]", DB_FETCHMODE_ASSOC); + + foreach ($res as $val) { + $keys = explode(', ', $val['index_keys']); + + if (sizeof($keys) > 1) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'multiple_key'); + } + } + + if (strpos($val['index_description'], 'primary key')) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'primary_key'); + } + } elseif (strpos($val['index_description'], 'unique')) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'unique_key'); + } + } + } + + // get auto_increment, not_null and timestamp + $res = $this->getAll("EXEC SP_COLUMNS[$table]", DB_FETCHMODE_ASSOC); + + foreach ($res as $val) { + $val = array_change_key_case($val, CASE_LOWER); + if ($val['nullable'] == '0') { + $this->_add_flag($flags[$val['column_name']], 'not_null'); + } + if (strpos($val['type_name'], 'identity')) { + $this->_add_flag($flags[$val['column_name']], 'auto_increment'); + } + if (strpos($val['type_name'], 'timestamp')) { + $this->_add_flag($flags[$val['column_name']], 'timestamp'); + } + } + } + + if (array_key_exists($column, $flags)) { + return(implode(' ', $flags[$column])); + } + return ''; + } + + // }}} + // {{{ _add_flag() + + /** + * Adds a string to the flags array if the flag is not yet in there + * - if there is no flag present the array is created. + * + * @param reference Reference to the flag-array + * @param value The flag value + * @access private + * @author Joern Barthel + */ + function _add_flag(&$array, $value) + { + if (!is_array($array)) { + $array = array($value); + } elseif (!in_array($value, $array)) { + array_push($array, $value); + } + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quote a string so it can be safely used as a table / column name + * + * Quoting style depends on which database driver is being used. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @since 1.6.0 + * @access public + */ + function quoteIdentifier($str) + { + return '[' . str_replace(']', ']]', $str) . ']'; + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/mysql.php b/thirdparty/pear/DB/mysql.php new file mode 100644 index 0000000..481b453 --- /dev/null +++ b/thirdparty/pear/DB/mysql.php @@ -0,0 +1,916 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +// XXX legend: +// +// XXX ERRORMSG: The error message from the mysql function should +// be registered here. +// +// TODO/wishlist: +// longReadlen +// binmode + + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's MySQL + * extension. + * + * This is for MySQL versions 4.0 and below. + * + * @package DB + * @version $Id$ + * @category Database + * @author Stig Bakken + */ +class DB_mysql extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); + var $num_rows = array(); + var $transaction_opcount = 0; + var $autocommit = true; + var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */ + var $_db = false; + + // }}} + // {{{ constructor + + /** + * DB_mysql constructor. + * + * @access public + */ + function DB_mysql() + { + $this->DB_common(); + $this->phptype = 'mysql'; + $this->dbsyntax = 'mysql'; + $this->features = array( + 'prepare' => false, + 'pconnect' => true, + 'transactions' => true, + 'limit' => 'alter' + ); + $this->errorcode_map = array( + 1004 => DB_ERROR_CANNOT_CREATE, + 1005 => DB_ERROR_CANNOT_CREATE, + 1006 => DB_ERROR_CANNOT_CREATE, + 1007 => DB_ERROR_ALREADY_EXISTS, + 1008 => DB_ERROR_CANNOT_DROP, + 1022 => DB_ERROR_ALREADY_EXISTS, + 1046 => DB_ERROR_NODBSELECTED, + 1048 => DB_ERROR_CONSTRAINT, + 1050 => DB_ERROR_ALREADY_EXISTS, + 1051 => DB_ERROR_NOSUCHTABLE, + 1054 => DB_ERROR_NOSUCHFIELD, + 1062 => DB_ERROR_ALREADY_EXISTS, + 1064 => DB_ERROR_SYNTAX, + 1100 => DB_ERROR_NOT_LOCKED, + 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, + 1146 => DB_ERROR_NOSUCHTABLE, + 1216 => DB_ERROR_CONSTRAINT, + 1217 => DB_ERROR_CONSTRAINT, + ); + } + + // }}} + // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param $persistent (optional) whether the connection should + * be persistent + * @access public + * @return int DB_OK on success, a DB error on failure + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('mysql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + $this->dsn = $dsninfo; + if ($dsninfo['protocol'] && $dsninfo['protocol'] == 'unix') { + $dbhost = ':' . $dsninfo['socket']; + } else { + $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; + if ($dsninfo['port']) { + $dbhost .= ':' . $dsninfo['port']; + } + } + + $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect'; + + if ($dbhost && $dsninfo['username'] && isset($dsninfo['password'])) { + $conn = @$connect_function($dbhost, $dsninfo['username'], + $dsninfo['password']); + } elseif ($dbhost && $dsninfo['username']) { + $conn = @$connect_function($dbhost, $dsninfo['username']); + } elseif ($dbhost) { + $conn = @$connect_function($dbhost); + } else { + $conn = false; + } + if (!$conn) { + if (($err = @mysql_error()) != '') { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, + null, $err); + } elseif (empty($php_errormsg)) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, + null, $php_errormsg); + } + } + + if ($dsninfo['database']) { + if (!@mysql_select_db($dsninfo['database'], $conn)) { + switch(mysql_errno($conn)) { + case 1049: + return $this->raiseError(DB_ERROR_NOSUCHDB, null, null, + null, @mysql_error($conn)); + case 1044: + return $this->raiseError(DB_ERROR_ACCESS_VIOLATION, null, null, + null, @mysql_error($conn)); + default: + return $this->raiseError(DB_ERROR, null, null, + null, @mysql_error($conn)); + } + } + // fix to allow calls to different databases in the same script + $this->_db = $dsninfo['database']; + } + + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Log out and disconnect from the database. + * + * @access public + * + * @return bool true on success, false if not connected. + */ + function disconnect() + { + $ret = @mysql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Send a query to MySQL and return the results as a MySQL resource + * identifier. + * + * @param the SQL query + * + * @access public + * + * @return mixed returns a valid MySQL result for successful SELECT + * queries, DB_OK for other successful queries. A DB error is + * returned on failure. + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + if ($this->_db) { + if (!@mysql_select_db($this->_db, $this->connection)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection); + $result = @mysql_query('BEGIN', $this->connection); + if (!$result) { + return $this->mysqlRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @mysql_query($query, $this->connection); + if (!$result) { + return $this->mysqlRaiseError(); + } + if (is_resource($result)) { + $numrows = $this->numrows($result); + if (is_object($numrows)) { + return $numrows; + } + $this->num_rows[(int)$result] = $numrows; + return $result; + } + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal mysql result pointer to the next available result + * + * This method has not been implemented yet. + * + * @param a valid sql result resource + * + * @access public + * + * @return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + if (!@mysql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @mysql_fetch_array($result, MYSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @mysql_fetch_row($result); + } + if (!$arr) { + // See: http://bugs.php.net/bug.php?id=22328 + // for why we can't check errors on fetching + return null; + /* + $errno = @mysql_errno($this->connection); + if (!$errno) { + return null; + } + return $this->mysqlRaiseError($errno); + */ + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + /* + * Even though this DBMS already trims output, we do this because + * a field might have intentional whitespace at the end that + * gets removed by DB_PORTABILITY_RTRIM under another driver. + */ + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Free the internal resources associated with $result. + * + * @param $result MySQL result identifier + * + * @access public + * + * @return bool true on success, false if $result is invalid + */ + function freeResult($result) + { + unset($this->num_rows[(int)$result]); + return @mysql_free_result($result); + } + + // }}} + // {{{ numCols() + + /** + * Get the number of columns in a result set. + * + * @param $result MySQL result identifier + * + * @access public + * + * @return int the number of columns per row in $result + */ + function numCols($result) + { + $cols = @mysql_num_fields($result); + + if (!$cols) { + return $this->mysqlRaiseError(); + } + + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Get the number of rows in a result set. + * + * @param $result MySQL result identifier + * + * @access public + * + * @return int the number of rows in $result + */ + function numRows($result) + { + $rows = @mysql_num_rows($result); + if ($rows === null) { + return $this->mysqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ autoCommit() + + /** + * Enable/disable automatic commits + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commit the current transaction. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + if ($this->_db) { + if (!@mysql_select_db($this->_db, $this->connection)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + $result = @mysql_query('COMMIT', $this->connection); + $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mysqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Roll back (undo) the current transaction. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + if ($this->_db) { + if (!@mysql_select_db($this->_db, $this->connection)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + $result = @mysql_query('ROLLBACK', $this->connection); + $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mysqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Gets the number of rows affected by the data manipulation + * query. For other queries, this function returns 0. + * + * @return number of rows affected by the last query + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + return @mysql_affected_rows($this->connection); + } else { + return 0; + } + } + + // }}} + // {{{ errorNative() + + /** + * Get the native error code of the last error (if any) that + * occured on the current connection. + * + * @access public + * + * @return int native MySQL error code + */ + function errorNative() + { + return @mysql_errno($this->connection); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + do { + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("UPDATE ${seqname} ". + 'SET id=LAST_INSERT_ID(id+1)'); + $this->popErrorHandling(); + if ($result === DB_OK) { + /** COMMON CASE **/ + $id = @mysql_insert_id($this->connection); + if ($id != 0) { + return $id; + } + /** EMPTY SEQ TABLE **/ + // Sequence table must be empty for some reason, so fill it and return 1 + // Obtain a user-level lock + $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + if ($result == 0) { + // Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error + return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); + } + + // add the default value + $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + + // Release the lock + $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + // We know what the result will be, so no need to try again + return 1; + + /** ONDEMAND TABLE CREATION **/ + } elseif ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) + { + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } else { + $repeat = 1; + } + + /** BACKWARDS COMPAT **/ + } elseif (DB::isError($result) && + $result->getCode() == DB_ERROR_ALREADY_EXISTS) + { + // see _BCsequence() comment + $result = $this->_BCsequence($seqname); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $repeat = 1; + } + } while ($repeat); + + return $this->raiseError($result); + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object is returned if + * problems arise. + * + * @internal + * @see DB_common::createSequence() + * @access public + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $res = $this->query("CREATE TABLE ${seqname} ". + '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'. + ' PRIMARY KEY(id))'); + if (DB::isError($res)) { + return $res; + } + // insert yields value 1, nextId call will generate ID 2 + $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)"); + if (DB::isError($res)) { + return $res; + } + // so reset to zero + return $this->query("UPDATE ${seqname} SET id = 0;"); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. DB_Error if problems. + * + * @internal + * @see DB_common::dropSequence() + * @access public + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ _BCsequence() + + /** + * Backwards compatibility with old sequence emulation implementation + * (clean up the dupes) + * + * @param string $seqname The sequence name to clean up + * @return mixed DB_Error or true + */ + function _BCsequence($seqname) + { + // Obtain a user-level lock... this will release any previous + // application locks, but unlike LOCK TABLES, it does not abort + // the current transaction and is much less frequently used. + $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); + if (DB::isError($result)) { + return $result; + } + if ($result == 0) { + // Failed to get the lock, can't do the conversion, bail + // with a DB_ERROR_NOT_LOCKED error + return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); + } + + $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); + if (DB::isError($highest_id)) { + return $highest_id; + } + // This should kill all rows except the highest + // We should probably do something if $highest_id isn't + // numeric, but I'm at a loss as how to handle that... + $result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id"); + if (DB::isError($result)) { + return $result; + } + + // If another thread has been waiting for this lock, + // it will go thru the above procedure, but will have no + // real effect + $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); + if (DB::isError($result)) { + return $result; + } + return true; + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quote a string so it can be safely used as a table or column name + * + * Quoting style depends on which database driver is being used. + * + * MySQL can't handle the backtick character (`) in + * table or column names. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @since 1.6.0 + * @access public + * @internal + */ + function quoteIdentifier($str) + { + return '`' . $str . '`'; + } + + // }}} + // {{{ quote() + + /** + * @deprecated Deprecated in release 1.6.0 + * @internal + */ + function quote($str) { + return $this->quoteSmart($str); + } + + // }}} + // {{{ escapeSimple() + + /** + * Escape a string according to the current DBMS's standards + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @internal + */ + function escapeSimple($str) { + if (function_exists('mysql_real_escape_string')) { + return @mysql_real_escape_string($str, $this->connection); + } else { + return @mysql_escape_string($str); + } + } + + // }}} + // {{{ modifyQuery() + + function modifyQuery($query) + { + if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { + // "DELETE FROM table" gives 0 affected rows in MySQL. + // This little hack lets you know how many rows were deleted. + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { + $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', + 'DELETE FROM \1 WHERE 1=1', $query); + } + } + return $query; + } + + // }}} + // {{{ modifyLimitQuery() + + function modifyLimitQuery($query, $from, $count, $params = array()) + { + if (DB::isManip($query)) { + return $query . " LIMIT $count"; + } else { + return $query . " LIMIT $from, $count"; + } + } + + // }}} + // {{{ mysqlRaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $errno PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see DB_common::errorCode() + * @see DB_common::raiseError() + */ + function mysqlRaiseError($errno = null) + { + if ($errno === null) { + if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { + $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT; + $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL; + $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT; + } else { + // Doing this in case mode changes during runtime. + $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS; + $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT; + $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS; + } + $errno = $this->errorCode(mysql_errno($this->connection)); + } + return $this->raiseError($errno, null, null, null, + @mysql_errno($this->connection) . ' ** ' . + @mysql_error($this->connection)); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table + * @param int $mode a valid tableInfo mode + * @return array an associative array with the information requested + * or an error object if something is wrong + * @access public + * @internal + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } elseif (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @mysql_list_fields($this->dsn['database'], + $result, $this->connection); + $got_string = true; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @mysql_num_fields($id); + + // made this IF due to performance (one if is faster than $count if's) + if (!$mode) { + for ($i=0; $i<$count; $i++) { + $res[$i]['table'] = $case_func(@mysql_field_table($id, $i)); + $res[$i]['name'] = $case_func(@mysql_field_name($id, $i)); + $res[$i]['type'] = @mysql_field_type($id, $i); + $res[$i]['len'] = @mysql_field_len($id, $i); + $res[$i]['flags'] = @mysql_field_flags($id, $i); + } + } else { // full + $res['num_fields']= $count; + + for ($i=0; $i<$count; $i++) { + $res[$i]['table'] = $case_func(@mysql_field_table($id, $i)); + $res[$i]['name'] = $case_func(@mysql_field_name($id, $i)); + $res[$i]['type'] = @mysql_field_type($id, $i); + $res[$i]['len'] = @mysql_field_len($id, $i); + $res[$i]['flags'] = @mysql_field_flags($id, $i); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + } + + // free the result only if we were called on a table + if ($got_string) { + @mysql_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Returns the query needed to get some backend info + * @param string $type What kind of info you want to retrieve + * @return string The SQL query string + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SHOW TABLES'; + case 'views': + return DB_ERROR_NOT_CAPABLE; + case 'users': + $sql = 'select distinct User from user'; + if ($this->dsn['database'] != 'mysql') { + $dsn = $this->dsn; + $dsn['database'] = 'mysql'; + if (DB::isError($db = DB::connect($dsn))) { + return $db; + } + $sql = $db->getCol($sql); + $db->disconnect(); + // XXX Fixme the mysql driver should take care of this + if (!@mysql_select_db($this->dsn['database'], $this->connection)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + return $sql; + case 'databases': + return 'SHOW DATABASES'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/mysqli.php b/thirdparty/pear/DB/mysqli.php new file mode 100644 index 0000000..fa15b44 --- /dev/null +++ b/thirdparty/pear/DB/mysqli.php @@ -0,0 +1,960 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +// EXPERIMENTAL + + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's mysqli + * extension. + * + * This is for MySQL versions 4.1 and above. Requires PHP 5. + * + * Note that persistent connections no longer exist. + * + * @package DB + * @version $Id$ + * @category Database + * @author Daniel Convissor + * @since Class functional since Release 1.6.3 + */ +class DB_mysqli extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); + var $num_rows = array(); + var $transaction_opcount = 0; + var $autocommit = true; + var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */ + var $_db = false; + + /** + * Array for converting MYSQLI_*_FLAG constants to text values + * @var array + * @access public + * @since Property available since Release 1.6.5 + */ + var $mysqli_flags = array( + MYSQLI_NOT_NULL_FLAG => 'not_null', + MYSQLI_PRI_KEY_FLAG => 'primary_key', + MYSQLI_UNIQUE_KEY_FLAG => 'unique_key', + MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key', + MYSQLI_BLOB_FLAG => 'blob', + MYSQLI_UNSIGNED_FLAG => 'unsigned', + MYSQLI_ZEROFILL_FLAG => 'zerofill', + MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment', + MYSQLI_TIMESTAMP_FLAG => 'timestamp', + MYSQLI_SET_FLAG => 'set', + // MYSQLI_NUM_FLAG => 'numeric', // unnecessary + // MYSQLI_PART_KEY_FLAG => 'multiple_key', // duplicatvie + MYSQLI_GROUP_FLAG => 'group_by' + ); + + /** + * Array for converting MYSQLI_TYPE_* constants to text values + * @var array + * @access public + * @since Property available since Release 1.6.5 + */ + var $mysqli_types = array( + MYSQLI_TYPE_DECIMAL => 'decimal', + MYSQLI_TYPE_TINY => 'tinyint', + MYSQLI_TYPE_SHORT => 'int', + MYSQLI_TYPE_LONG => 'int', + MYSQLI_TYPE_FLOAT => 'float', + MYSQLI_TYPE_DOUBLE => 'double', + // MYSQLI_TYPE_NULL => 'DEFAULT NULL', // let flags handle it + MYSQLI_TYPE_TIMESTAMP => 'timestamp', + MYSQLI_TYPE_LONGLONG => 'bigint', + MYSQLI_TYPE_INT24 => 'mediumint', + MYSQLI_TYPE_DATE => 'date', + MYSQLI_TYPE_TIME => 'time', + MYSQLI_TYPE_DATETIME => 'datetime', + MYSQLI_TYPE_YEAR => 'year', + MYSQLI_TYPE_NEWDATE => 'date', + MYSQLI_TYPE_ENUM => 'enum', + MYSQLI_TYPE_SET => 'set', + MYSQLI_TYPE_TINY_BLOB => 'tinyblob', + MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob', + MYSQLI_TYPE_LONG_BLOB => 'longblob', + MYSQLI_TYPE_BLOB => 'blob', + MYSQLI_TYPE_VAR_STRING => 'varchar', + MYSQLI_TYPE_STRING => 'char', + MYSQLI_TYPE_GEOMETRY => 'geometry', + ); + + // }}} + // {{{ constructor + + /** + * DB_mysql constructor. + * + * @access public + */ + function DB_mysqli() + { + $this->DB_common(); + $this->phptype = 'mysqli'; + $this->dbsyntax = 'mysqli'; + $this->features = array( + 'prepare' => false, + 'ssl' => true, + 'transactions' => true, + 'limit' => 'alter' + ); + $this->errorcode_map = array( + 1004 => DB_ERROR_CANNOT_CREATE, + 1005 => DB_ERROR_CANNOT_CREATE, + 1006 => DB_ERROR_CANNOT_CREATE, + 1007 => DB_ERROR_ALREADY_EXISTS, + 1008 => DB_ERROR_CANNOT_DROP, + 1022 => DB_ERROR_ALREADY_EXISTS, + 1046 => DB_ERROR_NODBSELECTED, + 1048 => DB_ERROR_CONSTRAINT, + 1050 => DB_ERROR_ALREADY_EXISTS, + 1051 => DB_ERROR_NOSUCHTABLE, + 1054 => DB_ERROR_NOSUCHFIELD, + 1062 => DB_ERROR_ALREADY_EXISTS, + 1064 => DB_ERROR_SYNTAX, + 1100 => DB_ERROR_NOT_LOCKED, + 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, + 1146 => DB_ERROR_NOSUCHTABLE, + 1216 => DB_ERROR_CONSTRAINT, + 1217 => DB_ERROR_CONSTRAINT, + ); + } + + // }}} + // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param string $dsn the data source name (see DB::parseDSN for syntax) + * @param boolean $persistent (optional) whether the connection should + * be persistent + * @return mixed DB_OK on success, a DB error on failure + * @access public + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('mysqli')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsninfo; + $conn = false; + @ini_set('track_errors', true); + + if ($this->getOption('ssl') === true) { + $init = mysqli_init(); + mysqli_ssl_set( + $init, + empty($dsninfo['key']) ? null : $dsninfo['key'], + empty($dsninfo['cert']) ? null : $dsninfo['cert'], + empty($dsninfo['ca']) ? null : $dsninfo['ca'], + empty($dsninfo['capath']) ? null : $dsninfo['capath'], + empty($dsninfo['cipher']) ? null : $dsninfo['cipher'] + ); + if ($conn = @mysqli_real_connect($init, + $dsninfo['hostspec'], + $dsninfo['username'], + $dsninfo['password'], + $dsninfo['database'], + $dsninfo['port'], + $dsninfo['socket'])) + { + $conn = $init; + } + } else { + $conn = @mysqli_connect( + $dsninfo['hostspec'], + $dsninfo['username'], + $dsninfo['password'], + $dsninfo['database'], + $dsninfo['port'], + $dsninfo['socket'] + ); + } + + @ini_restore('track_errors'); + + if (!$conn) { + if (($err = @mysqli_connect_error()) != '') { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, + null, $err); + } elseif (empty($php_errormsg)) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, + null, $php_errormsg); + } + } + + if ($dsninfo['database']) { + $this->_db = $dsninfo['database']; + } + + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Log out and disconnect from the database. + * + * @return boolean true on success, false if not connected + * @access public + */ + function disconnect() + { + $ret = @mysqli_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Send a query to MySQL and return the results as a MySQL resource + * identifier. + * + * @param string $query the SQL query + * @return mixed a valid MySQL result for successful SELECT + * queries, DB_OK for other successful queries. + * A DB error is returned on failure. + * @access public + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + if ($this->_db) { + if (!@mysqli_select_db($this->connection, $this->_db)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0'); + $result = @mysqli_query($this->connection, 'BEGIN'); + if (!$result) { + return $this->mysqlRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @mysqli_query($this->connection, $query); + if (!$result) { + return $this->mysqlRaiseError(); + } +# this next block is still sketchy.. + if (is_object($result)) { + $numrows = $this->numrows($result); + if (is_object($numrows)) { + return $numrows; + } +# need to come up with different means for next line +# since $result is object (int)$result won't fly... +// $this->num_rows[(int)$result] = $numrows; + return $result; + } + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal mysql result pointer to the next available result. + * + * This method has not been implemented yet. + * + * @param resource $result a valid sql result resource + * @return false + * @access public + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + if (!@mysqli_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @mysqli_fetch_row($result); + } + if (!$arr) { + $errno = @mysqli_errno($this->connection); + if (!$errno) { + return null; + } + return $this->mysqlRaiseError($errno); + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + /* + * Even though this DBMS already trims output, we do this because + * a field might have intentional whitespace at the end that + * gets removed by DB_PORTABILITY_RTRIM under another driver. + */ + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Free the internal resources associated with $result. + * + * @param resource $result MySQL result identifier + * @return bool true on success, false if $result is invalid + * @access public + */ + function freeResult($result) + { +# need to come up with different means for next line +# since $result is object (int)$result won't fly... +// unset($this->num_rows[(int)$result]); + return @mysqli_free_result($result); + } + + // }}} + // {{{ numCols() + + /** + * Get the number of columns in a result set. + * + * @param $result MySQL result identifier + * + * @access public + * + * @return int the number of columns per row in $result + */ + function numCols($result) + { + $cols = @mysqli_num_fields($result); + + if (!$cols) { + return $this->mysqlRaiseError(); + } + + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Get the number of rows in a result set. + * + * @param resource $result MySQL result identifier + * @return int the number of rows in $result + * @access public + */ + function numRows($result) + { + $rows = @mysqli_num_rows($result); + if ($rows === null) { + return $this->mysqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ autoCommit() + + /** + * Enable/disable automatic commits. + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commit the current transaction. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + if ($this->_db) { + if (!@mysqli_select_db($this->connection, $this->_db)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + $result = @mysqli_query($this->connection, 'COMMIT'); + $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mysqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Roll back (undo) the current transaction. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + if ($this->_db) { + if (!@mysqli_select_db($this->connection, $this->_db)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + $result = @mysqli_query($this->connection, 'ROLLBACK'); + $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mysqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Gets the number of rows affected by the data manipulation + * query. For other queries, this function returns 0. + * + * @return integer number of rows affected by the last query + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + return @mysqli_affected_rows($this->connection); + } else { + return 0; + } + } + + // }}} + // {{{ errorNative() + + /** + * Get the native error code of the last error (if any) that + * occured on the current connection. + * + * @return int native MySQL error code + * @access public + */ + function errorNative() + { + return @mysqli_errno($this->connection); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + do { + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("UPDATE ${seqname} ". + 'SET id=LAST_INSERT_ID(id+1)'); + $this->popErrorHandling(); + if ($result === DB_OK) { + /** COMMON CASE **/ + $id = @mysqli_insert_id($this->connection); + if ($id != 0) { + return $id; + } + /** EMPTY SEQ TABLE **/ + // Sequence table must be empty for some reason, so fill it and return 1 + // Obtain a user-level lock + $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + if ($result == 0) { + // Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error + return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); + } + + // add the default value + $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + + // Release the lock + $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + // We know what the result will be, so no need to try again + return 1; + + /** ONDEMAND TABLE CREATION **/ + } elseif ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) + { + $result = $this->createSequence($seq_name); + // Since createSequence initializes the ID to be 1, + // we do not need to retrieve the ID again (or we will get 2) + if (DB::isError($result)) { + return $this->raiseError($result); + } else { + // First ID of a newly created sequence is 1 + return 1; + } + + /** BACKWARDS COMPAT **/ + } elseif (DB::isError($result) && + $result->getCode() == DB_ERROR_ALREADY_EXISTS) + { + // see _BCsequence() comment + $result = $this->_BCsequence($seqname); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $repeat = 1; + } + } while ($repeat); + + return $this->raiseError($result); + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object is returned if + * problems arise. + * + * @internal + * @see DB_common::createSequence() + * @access public + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $res = $this->query("CREATE TABLE ${seqname} ". + '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'. + ' PRIMARY KEY(id))'); + if (DB::isError($res)) { + return $res; + } + // insert yields value 1, nextId call will generate ID 2 + return $this->query("INSERT INTO ${seqname} (id) VALUES (0)"); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. DB_Error if problems. + * + * @internal + * @see DB_common::dropSequence() + * @access public + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ _BCsequence() + + /** + * Backwards compatibility with old sequence emulation implementation + * (clean up the dupes). + * + * @param string $seqname The sequence name to clean up + * @return mixed DB_Error or true + */ + function _BCsequence($seqname) + { + // Obtain a user-level lock... this will release any previous + // application locks, but unlike LOCK TABLES, it does not abort + // the current transaction and is much less frequently used. + $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); + if (DB::isError($result)) { + return $result; + } + if ($result == 0) { + // Failed to get the lock, can't do the conversion, bail + // with a DB_ERROR_NOT_LOCKED error + return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); + } + + $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); + if (DB::isError($highest_id)) { + return $highest_id; + } + // This should kill all rows except the highest + // We should probably do something if $highest_id isn't + // numeric, but I'm at a loss as how to handle that... + $result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id"); + if (DB::isError($result)) { + return $result; + } + + // If another thread has been waiting for this lock, + // it will go thru the above procedure, but will have no + // real effect + $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); + if (DB::isError($result)) { + return $result; + } + return true; + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quote a string so it can be safely used as a table or column name + * + * Quoting style depends on which database driver is being used. + * + * MySQL can't handle the backtick character (`) in + * table or column names. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @since 1.6.0 + * @access public + * @internal + */ + function quoteIdentifier($str) + { + return '`' . $str . '`'; + } + + // }}} + // {{{ escapeSimple() + + /** + * Escape a string according to the current DBMS's standards + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @internal + */ + function escapeSimple($str) { + return @mysqli_real_escape_string($this->connection, $str); + } + + // }}} + // {{{ modifyQuery() + + function modifyQuery($query) + { + return $query; + } + + // }}} + // {{{ modifyLimitQuery() + + function modifyLimitQuery($query, $from, $count, $params = array()) + { + if (DB::isManip($query)) { + return $query . " LIMIT $count"; + } else { + return $query . " LIMIT $from, $count"; + } + } + + // }}} + // {{{ mysqlRaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $errno PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see DB_common::errorCode() + * @see DB_common::raiseError() + */ + function mysqlRaiseError($errno = null) + { + if ($errno === null) { + if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { + $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT; + $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL; + $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT; + } else { + // Doing this in case mode changes during runtime. + $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS; + $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT; + $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS; + } + $errno = $this->errorCode(mysqli_errno($this->connection)); + } + return $this->raiseError($errno, null, null, null, + @mysqli_errno($this->connection) . ' ** ' . + @mysqli_error($this->connection)); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set. + * + * WARNING: this method will probably not work because the mysqli_*() + * functions it relies upon may not exist. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table + * @param int $mode a valid tableInfo mode + * @return array an associative array with the information requested + * or an error object if something is wrong + * @access public + * @internal + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } elseif (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @mysqli_query($this->connection, + "SELECT * FROM $result LIMIT 0"); + $got_string = true; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_a($id, 'mysqli_result')) { + return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @mysqli_num_fields($id); + + // made this IF due to performance (one if is faster than $count if's) + if (!$mode) { + for ($i=0; $i<$count; $i++) { + $tmp = @mysqli_fetch_field($id); + $res[$i]['table'] = $case_func($tmp->table); + $res[$i]['name'] = $case_func($tmp->name); + $res[$i]['type'] = isset($this->mysqli_types[$tmp->type]) ? + $this->mysqli_types[$tmp->type] : + 'unknown'; + $res[$i]['len'] = $tmp->max_length; + + $res[$i]['flags'] = ''; + foreach ($this->mysqli_flags as $const => $means) { + if ($tmp->flags & $const) { + $res[$i]['flags'] .= $means . ' '; + } + } + if ($tmp->def) { + $res[$i]['flags'] .= 'default_' . rawurlencode($tmp->def); + } + $res[$i]['flags'] = trim($res[$i]['flags']); + } + } else { // full + $res['num_fields']= $count; + + for ($i=0; $i<$count; $i++) { + $tmp = @mysqli_fetch_field($id); + $res[$i]['table'] = $case_func($tmp->table); + $res[$i]['name'] = $case_func($tmp->name); + $res[$i]['type'] = isset($this->mysqli_types[$tmp->type]) ? + $this->mysqli_types[$tmp->type] : + 'unknown'; + $res[$i]['len'] = $tmp->max_length; + + $res[$i]['flags'] = ''; + foreach ($this->mysqli_flags as $const => $means) { + if ($tmp->flags & $const) { + $res[$i]['flags'] .= $means . ' '; + } + } + if ($tmp->def) { + $res[$i]['flags'] .= 'default_' . rawurlencode($tmp->def); + } + $res[$i]['flags'] = trim($res[$i]['flags']); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + } + + // free the result only if we were called on a table + if ($got_string) { + @mysqli_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Returns the query needed to get some backend info. + * + * @param string $type What kind of info you want to retrieve + * @return string The SQL query string + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SHOW TABLES'; + case 'views': + return DB_ERROR_NOT_CAPABLE; + case 'users': + $sql = 'select distinct User from user'; + if ($this->dsn['database'] != 'mysql') { + $dsn = $this->dsn; + $dsn['database'] = 'mysql'; + if (DB::isError($db = DB::connect($dsn))) { + return $db; + } + $sql = $db->getCol($sql); + $db->disconnect(); + // XXX Fixme the mysql driver should take care of this + if (!@mysqli_select_db($this->connection, $this->dsn['database'])) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + return $sql; + case 'databases': + return 'SHOW DATABASES'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/oci8.php b/thirdparty/pear/DB/oci8.php new file mode 100644 index 0000000..42f48b0 --- /dev/null +++ b/thirdparty/pear/DB/oci8.php @@ -0,0 +1,899 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +// be aware... OCIError() only appears to return anything when given a +// statement, so functions return the generic DB_ERROR instead of more +// useful errors that have to do with feedback from the database. + + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's Oracle 8 + * call-interface extension. + * + * Definitely works with versions 8 and 9 of Oracle. + * + * @package DB + * @version $Id$ + * @category Database + * @author James L. Pine + */ +class DB_oci8 extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $manip_query = array(); + var $prepare_types = array(); + var $autoCommit = 1; + var $last_stmt = false; + + /** + * stores the $data passed to execute() in the oci8 driver + * + * Gets reset to array() when simpleQuery() is run. + * + * Needed in case user wants to call numRows() after prepare/execute + * was used. + * + * @var array + * @access private + */ + var $_data = array(); + + // }}} + // {{{ constructor + + function DB_oci8() + { + $this->DB_common(); + $this->phptype = 'oci8'; + $this->dbsyntax = 'oci8'; + $this->features = array( + 'prepare' => false, + 'pconnect' => true, + 'transactions' => true, + 'limit' => 'alter' + ); + $this->errorcode_map = array( + 1 => DB_ERROR_CONSTRAINT, + 900 => DB_ERROR_SYNTAX, + 904 => DB_ERROR_NOSUCHFIELD, + 921 => DB_ERROR_SYNTAX, + 923 => DB_ERROR_SYNTAX, + 942 => DB_ERROR_NOSUCHTABLE, + 955 => DB_ERROR_ALREADY_EXISTS, + 1400 => DB_ERROR_CONSTRAINT_NOT_NULL, + 1407 => DB_ERROR_CONSTRAINT_NOT_NULL, + 1476 => DB_ERROR_DIVZERO, + 1722 => DB_ERROR_INVALID_NUMBER, + 2289 => DB_ERROR_NOSUCHTABLE, + 2291 => DB_ERROR_CONSTRAINT, + 2292 => DB_ERROR_CONSTRAINT, + 2449 => DB_ERROR_CONSTRAINT, + ); + } + + // }}} + // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param $persistent (optional) whether the connection should + * be persistent + * + * @return int DB_OK on success, a DB error code on failure + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('oci8')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + $this->dsn = $dsninfo; + + $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon'; + + if ($dsninfo['hostspec']) { + $conn = @$connect_function($dsninfo['username'], + $dsninfo['password'], + $dsninfo['hostspec']); + } elseif ($dsninfo['username'] || $dsninfo['password']) { + $conn = @$connect_function($dsninfo['username'], + $dsninfo['password']); + } else { + $conn = false; + } + if ($conn == false) { + $error = OCIError(); + $error = (is_array($error)) ? $error['message'] : null; + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, + null, $error); + } + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Log out and disconnect from the database. + * + * @return bool true on success, false if not connected. + */ + function disconnect() + { + $ret = @OCILogOff($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Send a query to oracle and return the results as an oci8 resource + * identifier. + * + * @param $query the SQL query + * + * @return int returns a valid oci8 result for successful SELECT + * queries, DB_OK for other successful queries. A DB error code + * is returned on failure. + */ + function simpleQuery($query) + { + $this->_data = array(); + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @OCIParse($this->connection, $query); + if (!$result) { + return $this->oci8RaiseError(); + } + if ($this->autoCommit) { + $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS); + } else { + $success = @OCIExecute($result,OCI_DEFAULT); + } + if (!$success) { + return $this->oci8RaiseError($result); + } + $this->last_stmt=$result; + // Determine which queries that should return data, and which + // should return an error code only. + return DB::isManip($query) ? DB_OK : $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal oracle result pointer to the next available result + * + * @param a valid oci8 result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && + $moredata) + { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS); + } + if (!$moredata) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Free the internal resources associated with $result. + * + * @param $result oci8 result identifier + * + * @return bool true on success, false if $result is invalid + */ + function freeResult($result) + { + return @OCIFreeStatement($result); + } + + /** + * Free the internal resources associated with a prepared query. + * + * @param $stmt oci8 statement identifier + * + * @return bool true on success, false if $result is invalid + */ + function freePrepared($stmt) + { + if (isset($this->prepare_types[(int)$stmt])) { + unset($this->prepare_types[(int)$stmt]); + unset($this->manip_query[(int)$stmt]); + } else { + return false; + } + return true; + } + + // }}} + // {{{ numRows() + + function numRows($result) + { + // emulate numRows for Oracle. yuck. + if ($this->options['portability'] & DB_PORTABILITY_NUMROWS && + $result === $this->last_stmt) + { + $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')'; + $save_query = $this->last_query; + $save_stmt = $this->last_stmt; + + if (count($this->_data)) { + $smt = $this->prepare('SELECT COUNT(*) FROM ('.$this->last_query.')'); + $count = $this->execute($smt, $this->_data); + } else { + $count =& $this->query($countquery); + } + + if (DB::isError($count) || + DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED))) + { + $this->last_query = $save_query; + $this->last_stmt = $save_stmt; + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + return $row[0]; + } + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ numCols() + + /** + * Get the number of columns in a result set. + * + * @param $result oci8 result identifier + * + * @return int the number of columns per row in $result + */ + function numCols($result) + { + $cols = @OCINumCols($result); + if (!$cols) { + return $this->oci8RaiseError($result); + } + return $cols; + } + + // }}} + // {{{ errorNative() + + /** + * Get the native error code of the last error (if any) that occured + * on the current connection. This does not work, as OCIError does + * not work unless given a statement. If OCIError does return + * something, so will this. + * + * @return int native oci8 error code + */ + function errorNative() + { + if (is_resource($this->last_stmt)) { + $error = @OCIError($this->last_stmt); + } else { + $error = @OCIError($this->connection); + } + if (is_array($error)) { + return $error['code']; + } + return false; + } + + // }}} + // {{{ prepare() + + /** + * Prepares a query for multiple execution with execute(). + * + * With oci8, this is emulated. + * + * prepare() requires a generic query as string like + * INSERT INTO numbers VALUES (?, ?, ?) + * . The ? characters are placeholders. + * + * Three types of placeholders can be used: + * + ? a quoted scalar value, i.e. strings, integers + * + ! value is inserted 'as is' + * + & requires a file name. The file's contents get + * inserted into the query (i.e. saving binary + * data in a db) + * + * Use backslashes to escape placeholder characters if you don't want + * them to be interpreted as placeholders. Example: + * "UPDATE foo SET col=? WHERE col='over \& under'" + * + * + * @param string $query query to be prepared + * @return mixed DB statement resource on success. DB_Error on failure. + */ + function prepare($query) + { + $tokens = preg_split('/((? $val) { + switch ($val) { + case '?': + $types[$token++] = DB_PARAM_SCALAR; + unset($tokens[$key]); + break; + case '&': + $types[$token++] = DB_PARAM_OPAQUE; + unset($tokens[$key]); + break; + case '!': + $types[$token++] = DB_PARAM_MISC; + unset($tokens[$key]); + break; + default: + $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); + if ($key != $binds) { + $newquery .= $tokens[$key] . ':bind' . $token; + } else { + $newquery .= $tokens[$key]; + } + } + } + + $this->last_query = $query; + $newquery = $this->modifyQuery($newquery); + if (!$stmt = @OCIParse($this->connection, $newquery)) { + return $this->oci8RaiseError(); + } + $this->prepare_types[(int)$stmt] = $types; + $this->manip_query[(int)$stmt] = DB::isManip($query); + return $stmt; + } + + // }}} + // {{{ execute() + + /** + * Executes a DB statement prepared with prepare(). + * + * @param resource $stmt a DB statement resource returned from prepare() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 for non-array items or the + * quantity of elements in the array. + * @return int returns an oci8 result resource for successful + * SELECT queries, DB_OK for other successful queries. A DB error + * code is returned on failure. + * @see DB_oci::prepare() + */ + function &execute($stmt, $data = array()) + { + if (!is_array($data)) { + $data = array($data); + } + + $this->_data = $data; + + $types =& $this->prepare_types[(int)$stmt]; + if (count($types) != count($data)) { + $tmp =& $this->raiseError(DB_ERROR_MISMATCH); + return $tmp; + } + + $i = 0; + foreach ($data as $key => $value) { + if ($types[$i] == DB_PARAM_MISC) { + /* + * Oracle doesn't seem to have the ability to pass a + * parameter along unchanged, so strip off quotes from start + * and end, plus turn two single quotes to one single quote, + * in order to avoid the quotes getting escaped by + * Oracle and ending up in the database. + */ + $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); + $data[$key] = str_replace("''", "'", $data[$key]); + } elseif ($types[$i] == DB_PARAM_OPAQUE) { + $fp = @fopen($data[$key], 'rb'); + if (!$fp) { + $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + return $tmp; + } + $data[$key] = fread($fp, filesize($data[$key])); + fclose($fp); + } + if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) { + $tmp = $this->oci8RaiseError($stmt); + return $tmp; + } + $i++; + } + if ($this->autoCommit) { + $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS); + } else { + $success = @OCIExecute($stmt, OCI_DEFAULT); + } + if (!$success) { + $tmp = $this->oci8RaiseError($stmt); + return $tmp; + } + $this->last_stmt = $stmt; + if ($this->manip_query[(int)$stmt]) { + $tmp = DB_OK; + } else { + $tmp =& new DB_result($this, $stmt); + } + return $tmp; + } + + // }}} + // {{{ autoCommit() + + /** + * Enable/disable automatic commits + * + * @param $onoff true/false whether to autocommit + */ + function autoCommit($onoff = false) + { + $this->autoCommit = (bool)$onoff;; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commit transactions on the current connection + * + * @return DB_ERROR or DB_OK + */ + function commit() + { + $result = @OCICommit($this->connection); + if (!$result) { + return $this->oci8RaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Roll back all uncommitted transactions on the current connection. + * + * @return DB_ERROR or DB_OK + */ + function rollback() + { + $result = @OCIRollback($this->connection); + if (!$result) { + return $this->oci8RaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Gets the number of rows affected by the last query. + * if the last query was a select, returns 0. + * + * @return number of rows affected by the last query or DB_ERROR + */ + function affectedRows() + { + if ($this->last_stmt === false) { + return $this->oci8RaiseError(); + } + $result = @OCIRowCount($this->last_stmt); + if ($result === false) { + return $this->oci8RaiseError($this->last_stmt); + } + return $result; + } + + // }}} + // {{{ modifyQuery() + + function modifyQuery($query) + { + // "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle + if (preg_match('/^\s*SELECT/i', $query) && + !preg_match('/\sFROM\s/i', $query)) { + $query .= ' FROM dual'; + } + return $query; + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Emulate the row limit support altering the query + * + * @param string $query The query to treat + * @param int $from The row to start to fetch from + * @param int $count The offset + * @return string The modified query + * + * @author Tomas V.V.Cox + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + // Let Oracle return the name of the columns instead of + // coding a "home" SQL parser + + if (count($params)) { + $result = $this->prepare("SELECT * FROM ($query) " + . 'WHERE NULL = NULL'); + $tmp =& $this->execute($result, $params); + } else { + $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL"; + + if (!$result = @OCIParse($this->connection, $q_fields)) { + $this->last_query = $q_fields; + return $this->oci8RaiseError(); + } + if (!@OCIExecute($result, OCI_DEFAULT)) { + $this->last_query = $q_fields; + return $this->oci8RaiseError($result); + } + } + + $ncols = OCINumCols($result); + $cols = array(); + for ( $i = 1; $i <= $ncols; $i++ ) { + $cols[] = '"' . OCIColumnName($result, $i) . '"'; + } + $fields = implode(', ', $cols); + // XXX Test that (tip by John Lim) + //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) { + // // Introduce the FIRST_ROWS Oracle query optimizer + // $query = substr($query, strlen($match[0]), strlen($query)); + // $query = "SELECT /* +FIRST_ROWS */ " . $query; + //} + + // Construct the query + // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2 + // Perhaps this could be optimized with the use of Unions + $query = "SELECT $fields FROM". + " (SELECT rownum as linenum, $fields FROM". + " ($query)". + ' WHERE rownum <= '. ($from + $count) . + ') WHERE linenum >= ' . ++$from; + return $query; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = 0; + do { + $this->expectError(DB_ERROR_NOSUCHTABLE); + $result =& $this->query("SELECT ${seqname}.nextval FROM dual"); + $this->popExpect(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + return $arr[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object is returned if + * problems arise. + * + * @internal + * @see DB_common::createSequence() + * @access public + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("CREATE SEQUENCE ${seqname}"); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. DB_Error if problems. + * + * @internal + * @see DB_common::dropSequence() + * @access public + */ + function dropSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("DROP SEQUENCE ${seqname}"); + } + + // }}} + // {{{ oci8RaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $errno PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see DB_common::errorCode() + * @see DB_common::raiseError() + */ + function oci8RaiseError($errno = null) + { + if ($errno === null) { + $error = @OCIError($this->connection); + return $this->raiseError($this->errorCode($error['code']), + null, null, null, $error['message']); + } elseif (is_resource($errno)) { + $error = @OCIError($errno); + return $this->raiseError($this->errorCode($error['code']), + null, null, null, $error['message']); + } + return $this->raiseError($this->errorCode($errno)); + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Returns the query needed to get some backend info + * @param string $type What kind of info you want to retrieve + * @return string The SQL query string + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT table_name FROM user_tables'; + default: + return null; + } + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set. + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * NOTE: flags won't contain index information. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table + * @param int $mode a valid tableInfo mode + * @return array an associative array with the information requested + * or an error object if something is wrong + * @access public + * @internal + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $result = strtoupper($result); + $q_fields = 'SELECT column_name, data_type, data_length, ' + . 'nullable ' + . 'FROM user_tab_columns ' + . "WHERE table_name='$result' ORDER BY column_id"; + + $this->last_query = $q_fields; + + if (!$stmt = @OCIParse($this->connection, $q_fields)) { + return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA); + } + if (!@OCIExecute($stmt, OCI_DEFAULT)) { + return $this->oci8RaiseError($stmt); + } + + $i = 0; + while (@OCIFetch($stmt)) { + $res[$i]['table'] = $case_func($result); + $res[$i]['name'] = $case_func(@OCIResult($stmt, 1)); + $res[$i]['type'] = @OCIResult($stmt, 2); + $res[$i]['len'] = @OCIResult($stmt, 3); + $res[$i]['flags'] = (@OCIResult($stmt, 4) == 'N') ? 'not_null' : ''; + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + $i++; + } + + if ($mode) { + $res['num_fields'] = $i; + } + @OCIFreeStatement($stmt); + + } else { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $result = $result->result; + } else { + /* + * ELSE, probably received a result resource identifier. + * Deprecated. Here for compatibility only. + */ + } + + if ($result === $this->last_stmt) { + $count = @OCINumCols($result); + + for ($i=0; $i<$count; $i++) { + $res[$i]['table'] = ''; + $res[$i]['name'] = $case_func(@OCIColumnName($result, $i+1)); + $res[$i]['type'] = @OCIColumnType($result, $i+1); + $res[$i]['len'] = @OCIColumnSize($result, $i+1); + $res[$i]['flags'] = ''; + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + if ($mode) { + $res['num_fields'] = $count; + } + + } else { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + } + return $res; + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/odbc.php b/thirdparty/pear/DB/odbc.php new file mode 100644 index 0000000..d0f50a1 --- /dev/null +++ b/thirdparty/pear/DB/odbc.php @@ -0,0 +1,585 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +// XXX legend: +// More info on ODBC errors could be found here: +// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp +// +// XXX ERRORMSG: The error message from the odbc function should +// be registered here. + + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's ODBC + * extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Stig Bakken + */ +class DB_odbc extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $row = array(); + + // }}} + // {{{ constructor + + function DB_odbc() + { + $this->DB_common(); + $this->phptype = 'odbc'; + $this->dbsyntax = 'sql92'; + $this->features = array( + 'prepare' => true, + 'pconnect' => true, + 'transactions' => false, + 'limit' => 'emulate' + ); + $this->errorcode_map = array( + '01004' => DB_ERROR_TRUNCATED, + '07001' => DB_ERROR_MISMATCH, + '21S01' => DB_ERROR_MISMATCH, + '21S02' => DB_ERROR_MISMATCH, + '22003' => DB_ERROR_INVALID_NUMBER, + '22005' => DB_ERROR_INVALID_NUMBER, + '22008' => DB_ERROR_INVALID_DATE, + '22012' => DB_ERROR_DIVZERO, + '23000' => DB_ERROR_CONSTRAINT, + '23502' => DB_ERROR_CONSTRAINT_NOT_NULL, + '23503' => DB_ERROR_CONSTRAINT, + '23505' => DB_ERROR_CONSTRAINT, + '24000' => DB_ERROR_INVALID, + '34000' => DB_ERROR_INVALID, + '37000' => DB_ERROR_SYNTAX, + '42000' => DB_ERROR_SYNTAX, + '42601' => DB_ERROR_SYNTAX, + 'IM001' => DB_ERROR_UNSUPPORTED, + 'S0000' => DB_ERROR_NOSUCHTABLE, + 'S0001' => DB_ERROR_ALREADY_EXISTS, + 'S0002' => DB_ERROR_NOSUCHTABLE, + 'S0011' => DB_ERROR_ALREADY_EXISTS, + 'S0012' => DB_ERROR_NOT_FOUND, + 'S0021' => DB_ERROR_ALREADY_EXISTS, + 'S0022' => DB_ERROR_NOSUCHFIELD, + 'S1000' => DB_ERROR_CONSTRAINT_NOT_NULL, + 'S1009' => DB_ERROR_INVALID, + 'S1090' => DB_ERROR_INVALID, + 'S1C00' => DB_ERROR_NOT_CAPABLE + ); + } + + // }}} + // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param $persistent (optional) whether the connection should + * be persistent + * + * @return int DB_OK on success, a DB error code on failure + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('odbc')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsninfo; + if ($dsninfo['dbsyntax']) { + $this->dbsyntax = $dsninfo['dbsyntax']; + } + switch ($this->dbsyntax) { + case 'solid': + $this->features = array( + 'prepare' => true, + 'pconnect' => true, + 'transactions' => true + ); + break; + case 'navision': + // the Navision driver doesn't support fetch row by number + $this->features['limit'] = false; + } + + /* + * This is hear for backwards compatibility. + * Should have been using 'database' all along, but used hostspec. + */ + if ($dsninfo['database']) { + $odbcdsn = $dsninfo['database']; + } elseif ($dsninfo['hostspec']) { + $odbcdsn = $dsninfo['hostspec']; + } else { + $odbcdsn = 'localhost'; + } + + if ($this->provides('pconnect')) { + $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect'; + } else { + $connect_function = 'odbc_connect'; + } + + if (empty($dsninfo['cursor'])) { + $conn = @$connect_function($odbcdsn, $dsninfo['username'], + $dsninfo['password']); + } else { + $conn = @$connect_function($odbcdsn, $dsninfo['username'], + $dsninfo['password'], + $dsninfo['cursor']); + } + + if (!is_resource($conn)) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, + null, $this->errorNative()); + } + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + function disconnect() + { + $err = @odbc_close($this->connection); + $this->connection = null; + return $err; + } + + // }}} + // {{{ simpleQuery() + + /** + * Send a query to ODBC and return the results as a ODBC resource + * identifier. + * + * @param $query the SQL query + * + * @return int returns a valid ODBC result for successful SELECT + * queries, DB_OK for other successful queries. A DB error code + * is returned on failure. + */ + function simpleQuery($query) + { + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @odbc_exec($this->connection, $query); + if (!$result) { + return $this->odbcRaiseError(); // XXX ERRORMSG + } + // Determine which queries that should return data, and which + // should return an error code only. + if (DB::isManip($query)) { + $this->manip_result = $result; // For affectedRows() + return DB_OK; + } + $this->row[(int)$result] = 0; + $this->manip_result = 0; + return $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal odbc result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return @odbc_next_result($result); + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + $arr = array(); + if ($rownum !== null) { + $rownum++; // ODBC first row is 1 + if (version_compare(phpversion(), '4.2.0', 'ge')) { + $cols = @odbc_fetch_into($result, $arr, $rownum); + } else { + $cols = @odbc_fetch_into($result, $rownum, $arr); + } + } else { + $cols = @odbc_fetch_into($result, $arr); + } + + if (!$cols) { + /* XXX FIXME: doesn't work with unixODBC and easysoft + (get corrupted $errno values) + if ($errno = @odbc_error($this->connection)) { + return $this->RaiseError($errno); + }*/ + return null; + } + if ($fetchmode !== DB_FETCHMODE_ORDERED) { + for ($i = 0; $i < count($arr); $i++) { + $colName = @odbc_field_name($result, $i+1); + $a[$colName] = $arr[$i]; + } + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $a = array_change_key_case($a, CASE_LOWER); + } + $arr = $a; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + function freeResult($result) + { + unset($this->row[(int)$result]); + return @odbc_free_result($result); + } + + // }}} + // {{{ numCols() + + function numCols($result) + { + $cols = @odbc_num_fields($result); + if (!$cols) { + return $this->odbcRaiseError(); + } + return $cols; + } + + // }}} + // {{{ affectedRows() + + /** + * Returns the number of rows affected by a manipulative query + * (INSERT, DELETE, UPDATE) + * @return mixed int affected rows, 0 when non manip queries or + * DB error on error + */ + function affectedRows() + { + if (empty($this->manip_result)) { // In case of SELECT stms + return 0; + } + $nrows = @odbc_num_rows($this->manip_result); + if ($nrows == -1) { + return $this->odbcRaiseError(); + } + return $nrows; + } + + // }}} + // {{{ numRows() + + /** + * ODBC may or may not support counting rows in the result set of + * SELECTs. + * + * @param $result the odbc result resource + * @return the number of rows, or 0 + */ + function numRows($result) + { + $nrows = @odbc_num_rows($result); + if ($nrows == -1) { + return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED); + } + return $nrows; + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quote a string so it can be safely used as a table / column name + * + * Quoting style depends on which dbsyntax was passed in the DSN. + * + * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked + * "Use ANSI quoted identifiers" when setting up the ODBC data source. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @since 1.6.0 + * @access public + */ + function quoteIdentifier($str) + { + switch ($this->dsn['dbsyntax']) { + case 'access': + return '[' . $str . ']'; + case 'mssql': + case 'sybase': + return '[' . str_replace(']', ']]', $str) . ']'; + case 'mysql': + case 'mysqli': + return '`' . $str . '`'; + default: + return '"' . str_replace('"', '""', $str) . '"'; + } + } + + // }}} + // {{{ quote() + + /** + * @deprecated Deprecated in release 1.6.0 + * @internal + */ + function quote($str) { + return $this->quoteSmart($str); + } + + // }}} + // {{{ errorNative() + + /** + * Get the native error code of the last error (if any) that + * occured on the current connection. + * + * @access public + * + * @return int ODBC error code + */ + function errorNative() + { + if (!isset($this->connection) || !is_resource($this->connection)) { + return @odbc_error() . ' ' . @odbc_errormsg(); + } + return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("update ${seqname} set id = id + 1"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->createSequence($seq_name); + $this->popErrorHandling(); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $result = $this->query("insert into ${seqname} (id) values(0)"); + } else { + $repeat = 0; + } + } while ($repeat); + + if (DB::isError($result)) { + return $this->raiseError($result); + } + + $result = $this->query("select id from ${seqname}"); + if (DB::isError($result)) { + return $result; + } + + $row = $result->fetchRow(DB_FETCHMODE_ORDERED); + if (DB::isError($row || !$row)) { + return $row; + } + + return $row[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object is returned if + * problems arise. + * + * @internal + * @see DB_common::createSequence() + * @access public + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("CREATE TABLE ${seqname} ". + '(id integer NOT NULL,'. + ' PRIMARY KEY(id))'); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. DB_Error if problems. + * + * @internal + * @see DB_common::dropSequence() + * @access public + */ + function dropSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("DROP TABLE ${seqname}"); + } + + // }}} + // {{{ autoCommit() + + function autoCommit($onoff = false) + { + if (!@odbc_autocommit($this->connection, $onoff)) { + return $this->odbcRaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ commit() + + function commit() + { + if (!@odbc_commit($this->connection)) { + return $this->odbcRaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + function rollback() + { + if (!@odbc_rollback($this->connection)) { + return $this->odbcRaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ odbcRaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $errno PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see errorNative() + * @see DB_common::errorCode() + * @see DB_common::raiseError() + */ + function odbcRaiseError($errno = null) + { + if ($errno === null) { + switch ($this->dbsyntax) { + case 'access': + if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { + $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD; + } else { + // Doing this in case mode changes during runtime. + $this->errorcode_map['07001'] = DB_ERROR_MISMATCH; + } + } + $errno = $this->errorCode(odbc_error($this->connection)); + } + return $this->raiseError($errno, null, null, null, + $this->errorNative()); + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/pgsql.php b/thirdparty/pear/DB/pgsql.php new file mode 100644 index 0000000..2b011c9 --- /dev/null +++ b/thirdparty/pear/DB/pgsql.php @@ -0,0 +1,847 @@ + | +// | Stig Bakken | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's PostgreSQL + * extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Rui Hirokawa + * @author Stig Bakken + */ +class DB_pgsql extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); + var $transaction_opcount = 0; + var $dsn = array(); + var $row = array(); + var $num_rows = array(); + var $affected = 0; + var $autocommit = true; + var $fetchmode = DB_FETCHMODE_ORDERED; + + // }}} + // {{{ constructor + + function DB_pgsql() + { + $this->DB_common(); + $this->phptype = 'pgsql'; + $this->dbsyntax = 'pgsql'; + $this->features = array( + 'prepare' => false, + 'pconnect' => true, + 'transactions' => true, + 'limit' => 'alter' + ); + $this->errorcode_map = array( + ); + } + + // }}} + // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param $persistent (optional) whether the connection should + * be persistent + * + * @return int DB_OK on success, a DB error code on failure. + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('pgsql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsninfo; + $protocol = $dsninfo['protocol'] ? $dsninfo['protocol'] : 'tcp'; + $connstr = ''; + + if ($protocol == 'tcp') { + if ($dsninfo['hostspec']) { + $connstr .= 'host=' . $dsninfo['hostspec']; + } + if ($dsninfo['port']) { + $connstr .= ' port=' . $dsninfo['port']; + } + } elseif ($protocol == 'unix') { + // Allow for pg socket in non-standard locations. + if ($dsninfo['socket']) { + $connstr .= 'host=' . $dsninfo['socket']; + } + if ($dsninfo['port']) { + $connstr .= ' port=' . $dsninfo['port']; + } + } + + if ($dsninfo['database']) { + $connstr .= ' dbname=\'' . addslashes($dsninfo['database']) . '\''; + } + if ($dsninfo['username']) { + $connstr .= ' user=\'' . addslashes($dsninfo['username']) . '\''; + } + if ($dsninfo['password']) { + $connstr .= ' password=\'' . addslashes($dsninfo['password']) . '\''; + } + if (!empty($dsninfo['options'])) { + $connstr .= ' options=' . $dsninfo['options']; + } + if (!empty($dsninfo['tty'])) { + $connstr .= ' tty=' . $dsninfo['tty']; + } + + $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect'; + + $ini = ini_get('track_errors'); + if ($ini) { + $conn = @$connect_function($connstr); + } else { + ini_set('track_errors', 1); + $conn = @$connect_function($connstr); + ini_set('track_errors', $ini); + } + if ($conn == false) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, + null, null, strip_tags($php_errormsg)); + } + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Log out and disconnect from the database. + * + * @return bool true on success, false if not connected. + */ + function disconnect() + { + $ret = @pg_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Send a query to PostgreSQL and return the results as a + * PostgreSQL resource identifier. + * + * @param $query the SQL query + * + * @return int returns a valid PostgreSQL result for successful SELECT + * queries, DB_OK for other successful queries. A DB error code + * is returned on failure. + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @pg_exec($this->connection, 'begin;'); + if (!$result) { + return $this->pgsqlRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @pg_exec($this->connection, $query); + if (!$result) { + return $this->pgsqlRaiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + if ($ismanip) { + $this->affected = @pg_cmdtuples($result); + return DB_OK; + } elseif (preg_match('/^\s*\(?\s*(SELECT(?!\s+INTO)|EXPLAIN|SHOW)\s/si', $query)) { + /* PostgreSQL commands: + ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY, + CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH, + GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET, + REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW, + UNLISTEN, UPDATE, VACUUM + */ + $this->row[(int)$result] = 0; // reset the row counter. + $numrows = $this->numrows($result); + if (is_object($numrows)) { + return $numrows; + } + $this->num_rows[(int)$result] = $numrows; + $this->affected = 0; + return $result; + } else { + $this->affected = 0; + return DB_OK; + } + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal pgsql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ errorCode() + + /** + * Determine PEAR::DB error code from the database's text error message. + * + * @param string $errormsg error message returned from the database + * @return integer an error number from a DB error constant + */ + function errorCode($errormsg) + { + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/(([Rr]elation|[Ss]equence|[Tt]able)( [\"\'].*[\"\'])? does not exist|[Cc]lass ".+" not found)$/' => DB_ERROR_NOSUCHTABLE, + '/[Cc]olumn [\"\'].*[\"\'] .*does not exist/' => DB_ERROR_NOSUCHFIELD, + '/[Rr]elation [\"\'].*[\"\'] already exists|[Cc]annot insert a duplicate key into (a )?unique index.*/' => DB_ERROR_ALREADY_EXISTS, + '/(divide|division) by zero$/' => DB_ERROR_DIVZERO, + '/pg_atoi: error in .*: can\'t parse /' => DB_ERROR_INVALID_NUMBER, + '/invalid input syntax for integer/' => DB_ERROR_INVALID_NUMBER, + '/ttribute [\"\'].*[\"\'] not found$|[Rr]elation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => DB_ERROR_NOSUCHFIELD, + '/parser: parse error at or near \"/' => DB_ERROR_SYNTAX, + '/syntax error at/' => DB_ERROR_SYNTAX, + '/permission denied/' => DB_ERROR_ACCESS_VIOLATION, + '/violates not-null constraint/' => DB_ERROR_CONSTRAINT_NOT_NULL, + '/violates [\w ]+ constraint/' => DB_ERROR_CONSTRAINT, + '/referential integrity violation/' => DB_ERROR_CONSTRAINT + ); + } + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + $result_int = (int)$result; + $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int]; + if ($rownum >= $this->num_rows[$result_int]) { + return null; + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @pg_fetch_row($result, $rownum); + } + if (!$arr) { + $err = pg_errormessage($this->connection); + if (!$err) { + return null; + } + return $this->pgsqlRaiseError(); + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + $this->row[$result_int] = ++$rownum; + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Free the internal resources associated with $result. + * + * @param $result int PostgreSQL result identifier + * + * @return bool true on success, false if $result is invalid + */ + function freeResult($result) + { + if (is_resource($result)) { + unset($this->row[(int)$result]); + unset($this->num_rows[(int)$result]); + $this->affected = 0; + return @pg_freeresult($result); + } + return false; + } + + // }}} + // {{{ quote() + + /** + * @deprecated Deprecated in release 1.6.0 + * @internal + */ + function quote($str) { + return $this->quoteSmart($str); + } + + // }}} + // {{{ quoteSmart() + + /** + * Format input so it can be safely used in a query + * + * @param mixed $in data to be quoted + * + * @return mixed Submitted variable's type = returned value: + * + null = the string NULL + * + boolean = string TRUE or FALSE + * + integer or double = the unquoted number + * + other (including strings and numeric strings) = + * the data escaped according to MySQL's settings + * then encapsulated between single quotes + * + * @internal + */ + function quoteSmart($in) + { + if (is_int($in) || is_double($in)) { + return $in; + } elseif (is_bool($in)) { + return $in ? 'TRUE' : 'FALSE'; + } elseif (is_null($in)) { + return 'NULL'; + } else { + return "'" . $this->escapeSimple($in) . "'"; + } + } + + // }}} + // {{{ escapeSimple() + + /** + * Escape a string according to the current DBMS's standards + * + * PostgreSQL treats a backslash as an escape character, so they are + * removed. + * + * Not using pg_escape_string() yet because it requires PostgreSQL + * to be at version 7.2 or greater. + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @internal + */ + function escapeSimple($str) { + return str_replace("'", "''", str_replace('\\', '\\\\', $str)); + } + + // }}} + // {{{ numCols() + + /** + * Get the number of columns in a result set. + * + * @param $result resource PostgreSQL result identifier + * + * @return int the number of columns per row in $result + */ + function numCols($result) + { + $cols = @pg_numfields($result); + if (!$cols) { + return $this->pgsqlRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Get the number of rows in a result set. + * + * @param $result resource PostgreSQL result identifier + * + * @return int the number of rows in $result + */ + function numRows($result) + { + $rows = @pg_numrows($result); + if ($rows === null) { + return $this->pgsqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ errorNative() + + /** + * Get the native error code of the last error (if any) that + * occured on the current connection. + * + * @return int native PostgreSQL error code + */ + function errorNative() + { + return pg_errormessage($this->connection); + } + + // }}} + // {{{ autoCommit() + + /** + * Enable/disable automatic commits + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commit the current transaction. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + // (disabled) hack to shut up error messages from libpq.a + //@fclose(@fopen("php://stderr", "w")); + $result = @pg_exec($this->connection, 'end;'); + $this->transaction_opcount = 0; + if (!$result) { + return $this->pgsqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Roll back (undo) the current transaction. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + $result = @pg_exec($this->connection, 'abort;'); + $this->transaction_opcount = 0; + if (!$result) { + return $this->pgsqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Gets the number of rows affected by the last query. + * if the last query was a select, returns 0. + * + * @return int number of rows affected by the last query or DB_ERROR + */ + function affectedRows() + { + return $this->affected; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = false; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result =& $this->query("SELECT NEXTVAL('${seqname}')"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = true; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->createSequence($seq_name); + $this->popErrorHandling(); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + $result->free(); + return $arr[0]; + } + + // }}} + // {{{ createSequence() + + /** + * Create the sequence + * + * @param string $seq_name the name of the sequence + * @return mixed DB_OK on success or DB error on error + * @access public + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $result = $this->query("CREATE SEQUENCE ${seqname}"); + return $result; + } + + // }}} + // {{{ dropSequence() + + /** + * Drop a sequence + * + * @param string $seq_name the name of the sequence + * @return mixed DB_OK on success or DB error on error + * @access public + */ + function dropSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("DROP SEQUENCE ${seqname}"); + } + + // }}} + // {{{ modifyLimitQuery() + + function modifyLimitQuery($query, $from, $count, $params = array()) + { + $query = $query . " LIMIT $count OFFSET $from"; + return $query; + } + + // }}} + // {{{ pgsqlRaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $errno PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see errorNative() + * @see errorCode() + * @see DB_common::raiseError() + */ + function pgsqlRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $err = $this->errorCode($native); + } else { + $err = $errno; + } + return $this->raiseError($err, null, null, null, $native); + } + + // }}} + // {{{ _pgFieldFlags() + + /** + * Flags of a Field + * + * @param int $resource PostgreSQL result identifier + * @param int $num_field the field number + * + * @return string The flags of the field ("not_null", "default_value", + * "primary_key", "unique_key" and "multiple_key" + * are supported). The default value is passed + * through rawurlencode() in case there are spaces in it. + * @access private + */ + function _pgFieldFlags($resource, $num_field, $table_name) + { + $field_name = @pg_fieldname($resource, $num_field); + + $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef + FROM pg_attribute f, pg_class tab, pg_type typ + WHERE tab.relname = typ.typname + AND typ.typrelid = f.attrelid + AND f.attname = '$field_name' + AND tab.relname = '$table_name'"); + if (@pg_numrows($result) > 0) { + $row = @pg_fetch_row($result, 0); + $flags = ($row[0] == 't') ? 'not_null ' : ''; + + if ($row[1] == 't') { + $result = @pg_exec($this->connection, "SELECT a.adsrc + FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a + WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid + AND f.attrelid = a.adrelid AND f.attname = '$field_name' + AND tab.relname = '$table_name' AND f.attnum = a.adnum"); + $row = @pg_fetch_row($result, 0); + $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]); + $flags .= 'default_' . rawurlencode($num) . ' '; + } + } else { + $flags = ''; + } + $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey + FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i + WHERE tab.relname = typ.typname + AND typ.typrelid = f.attrelid + AND f.attrelid = i.indrelid + AND f.attname = '$field_name' + AND tab.relname = '$table_name'"); + $count = @pg_numrows($result); + + for ($i = 0; $i < $count ; $i++) { + $row = @pg_fetch_row($result, $i); + $keys = explode(' ', $row[2]); + + if (in_array($num_field + 1, $keys)) { + $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : ''; + $flags .= ($row[1] == 't') ? 'primary_key ' : ''; + if (count($keys) > 1) + $flags .= 'multiple_key '; + } + } + + return trim($flags); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set. + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table + * @param int $mode a valid tableInfo mode + * @return array an associative array with the information requested + * or an error object if something is wrong + * @access public + * @internal + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } elseif (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0"); + $got_string = true; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @pg_numfields($id); + + // made this IF due to performance (one if is faster than $count if's) + if (!$mode) { + + for ($i=0; $i<$count; $i++) { + $res[$i]['table'] = $got_string ? $case_func($result) : ''; + $res[$i]['name'] = $case_func(@pg_fieldname($id, $i)); + $res[$i]['type'] = @pg_fieldtype($id, $i); + $res[$i]['len'] = @pg_fieldsize($id, $i); + $res[$i]['flags'] = $got_string ? $this->_pgFieldflags($id, $i, $result) : ''; + } + + } else { // full + $res['num_fields']= $count; + + for ($i=0; $i<$count; $i++) { + $res[$i]['table'] = $got_string ? $case_func($result) : ''; + $res[$i]['name'] = $case_func(@pg_fieldname($id, $i)); + $res[$i]['type'] = @pg_fieldtype($id, $i); + $res[$i]['len'] = @pg_fieldsize($id, $i); + $res[$i]['flags'] = $got_string ? $this->_pgFieldFlags($id, $i, $result) : ''; + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + } + + // free the result only if we were called on a table + if ($got_string) { + @pg_freeresult($id); + } + return $res; + } + + // }}} + // {{{ getTablesQuery() + + /** + * Returns the query needed to get some backend info + * @param string $type What kind of info you want to retrieve + * @return string The SQL query string + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return "SELECT c.relname as \"Name\" + FROM pg_class c, pg_user u + WHERE c.relowner = u.usesysid AND c.relkind = 'r' + AND not exists (select 1 from pg_views where viewname = c.relname) + AND c.relname !~ '^(pg_|sql_)' + UNION + SELECT c.relname as \"Name\" + FROM pg_class c + WHERE c.relkind = 'r' + AND not exists (select 1 from pg_views where viewname = c.relname) + AND not exists (select 1 from pg_user where usesysid = c.relowner) + AND c.relname !~ '^pg_'"; + case 'views': + // Table cols: viewname | viewowner | definition + return 'SELECT viewname FROM pg_views'; + case 'users': + // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil + return 'SELECT usename FROM pg_user'; + case 'databases': + return 'SELECT datname FROM pg_database'; + case 'functions': + return 'SELECT proname FROM pg_proc'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/sqlite.php b/thirdparty/pear/DB/sqlite.php new file mode 100644 index 0000000..ef5535b --- /dev/null +++ b/thirdparty/pear/DB/sqlite.php @@ -0,0 +1,695 @@ + | +// | Mika Tuupola | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for the SQLite + * PECL extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Urs Gehrig + * @author Mika Tuupola + */ +class DB_sqlite extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); + var $_lasterror = ''; + + // }}} + // {{{ constructor + + /** + * Constructor for this class. + * + * Error codes according to sqlite_exec. Error Codes specification is + * in the {@link http://sqlite.org/c_interface.html online manual}. + * + * This errorhandling based on sqlite_exec is not yet implemented. + * + * @access public + */ + function DB_sqlite() + { + + $this->DB_common(); + $this->phptype = 'sqlite'; + $this->dbsyntax = 'sqlite'; + $this->features = array ( + 'prepare' => false, + 'pconnect' => true, + 'transactions' => false, + 'limit' => 'alter' + ); + + // SQLite data types, http://www.sqlite.org/datatypes.html + $this->keywords = array ( + 'BLOB' => '', + 'BOOLEAN' => '', + 'CHARACTER' => '', + 'CLOB' => '', + 'FLOAT' => '', + 'INTEGER' => '', + 'KEY' => '', + 'NATIONAL' => '', + 'NUMERIC' => '', + 'NVARCHAR' => '', + 'PRIMARY' => '', + 'TEXT' => '', + 'TIMESTAMP' => '', + 'UNIQUE' => '', + 'VARCHAR' => '', + 'VARYING' => '' + ); + $this->errorcode_map = array( + ); + } + + // }}} + // {{{ connect() + + /** + * Connect to a database represented by a file. + * + * @param $dsn the data source name; the file is taken as + * database; "sqlite://root:@host/test.db?mode=0644" + * @param $persistent (optional) whether the connection should + * be persistent + * @access public + * @return int DB_OK on success, a DB error on failure + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('sqlite')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsninfo; + + if ($dsninfo['database']) { + if (!file_exists($dsninfo['database'])) { + if (!touch($dsninfo['database'])) { + return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); + } + if (!isset($dsninfo['mode']) || + !is_numeric($dsninfo['mode'])) + { + $mode = 0644; + } else { + $mode = octdec($dsninfo['mode']); + } + if (!chmod($dsninfo['database'], $mode)) { + return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); + } + if (!file_exists($dsninfo['database'])) { + return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); + } + } + if (!is_file($dsninfo['database'])) { + return $this->sqliteRaiseError(DB_ERROR_INVALID); + } + if (!is_readable($dsninfo['database'])) { + return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); + } + } else { + return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); + } + + $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open'; + if (!($conn = @$connect_function($dsninfo['database']))) { + return $this->sqliteRaiseError(DB_ERROR_NODBSELECTED); + } + $this->connection = $conn; + + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Log out and disconnect from the database. + * + * @access public + * @return bool true on success, false if not connected. + * @todo fix return values + */ + function disconnect() + { + $ret = @sqlite_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Send a query to SQLite and returns the results as a SQLite resource + * identifier. + * + * @param the SQL query + * @access public + * @return mixed returns a valid SQLite result for successful SELECT + * queries, DB_OK for other successful queries. A DB error is + * returned on failure. + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->_modifyQuery($query); + ini_set('track_errors', true); + $result = @sqlite_query($query, $this->connection); + ini_restore('track_errors'); + $this->_lasterror = isset($php_errormsg) ? $php_errormsg : ''; + $this->result = $result; + if (!$this->result) { + return $this->sqliteRaiseError(null); + } + + /* sqlite_query() seems to allways return a resource */ + /* so cant use that. Using $ismanip instead */ + if (!$ismanip) { + $numRows = $this->numRows($result); + + /* if numRows() returned PEAR_Error */ + if (is_object($numRows)) { + return $numRows; + } + return $result; + } + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal sqlite result pointer to the next available result. + * + * @param a valid sqlite result resource + * @access public + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + if (!@sqlite_seek($this->result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @sqlite_fetch_array($result, SQLITE_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @sqlite_fetch_array($result, SQLITE_NUM); + } + if (!$arr) { + /* See: http://bugs.php.net/bug.php?id=22328 */ + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + /* + * Even though this DBMS already trims output, we do this because + * a field might have intentional whitespace at the end that + * gets removed by DB_PORTABILITY_RTRIM under another driver. + */ + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Free the internal resources associated with $result. + * + * @param $result SQLite result identifier + * @access public + * @return bool true on success, false if $result is invalid + */ + function freeResult(&$result) + { + // XXX No native free? + if (!is_resource($result)) { + return false; + } + $result = null; + return true; + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set. + * + * @return number of columns in a result set + */ + function numCols($result) + { + $cols = @sqlite_num_fields($result); + if (!$cols) { + return $this->sqliteRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows affected by a query. + * + * @return number of rows affected by the last query + */ + function numRows($result) + { + $rows = @sqlite_num_rows($result); + if (!is_integer($rows)) { + return $this->raiseError(); + } + return $rows; + } + + // }}} + // {{{ affected() + + /** + * Gets the number of rows affected by a query. + * + * @return number of rows affected by the last query + */ + function affectedRows() + { + return @sqlite_changes($this->connection); + } + + // }}} + // {{{ errorNative() + + /** + * Get the native error string of the last error (if any) that + * occured on the current connection. + * + * This is used to retrieve more meaningfull error messages DB_pgsql + * way since sqlite_last_error() does not provide adequate info. + * + * @return string native SQLite error message + */ + function errorNative() + { + return($this->_lasterror); + } + + // }}} + // {{{ errorCode() + + /** + * Determine PEAR::DB error code from the database's text error message. + * + * @param string $errormsg error message returned from the database + * @return integer an error number from a DB error constant + */ + function errorCode($errormsg) + { + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/^no such table:/' => DB_ERROR_NOSUCHTABLE, + '/^table .* already exists$/' => DB_ERROR_ALREADY_EXISTS, + '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT, + '/is not unique/' => DB_ERROR_CONSTRAINT, + '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT, + '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL, + '/^no such column:/' => DB_ERROR_NOSUCHFIELD, + '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX + ); + } + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. DB_Error if problems. + * + * @internal + * @see DB_common::dropSequence() + * @access public + */ + function dropSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("DROP TABLE $seqname"); + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object is returned if + * problems arise. + * + * @internal + * @see DB_common::createSequence() + * @access public + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $query = 'CREATE TABLE ' . $seqname . + ' (id INTEGER UNSIGNED PRIMARY KEY) '; + $result = $this->query($query); + if (DB::isError($result)) { + return($result); + } + $query = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname + BEGIN + DELETE FROM $seqname WHERE idquery($query); + if (DB::isError($result)) { + return($result); + } + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + + do { + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)"); + $this->popErrorHandling(); + if ($result === DB_OK) { + $id = @sqlite_last_insert_rowid($this->connection); + if ($id != 0) { + return $id; + } + } elseif ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) + { + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } else { + $repeat = 1; + } + } + } while ($repeat); + + return $this->raiseError($result); + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Returns the query needed to get some backend info. + * + * Refer to the online manual at http://sqlite.org/sqlite.html. + * + * @param string $type What kind of info you want to retrieve + * @return string The SQL query string + */ + function getSpecialQuery($type, $args=array()) + { + if (!is_array($args)) + return $this->raiseError('no key specified', null, null, null, + 'Argument has to be an array.'); + switch (strtolower($type)) { + case 'master': + return 'SELECT * FROM sqlite_master;'; + case 'tables': + return "SELECT name FROM sqlite_master WHERE type='table' " + . 'UNION ALL SELECT name FROM sqlite_temp_master ' + . "WHERE type='table' ORDER BY name;"; + case 'schema': + return 'SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL ' + . 'SELECT * FROM sqlite_temp_master) ' + . "WHERE type!='meta' ORDER BY tbl_name, type DESC, name;"; + case 'schemax': + case 'schema_x': + /* + * Use like: + * $res = $db->query($db->getSpecialQuery('schema_x', array('table' => 'table3'))); + */ + return 'SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL ' + . 'SELECT * FROM sqlite_temp_master) ' + . "WHERE tbl_name LIKE '{$args['table']}' AND type!='meta' " + . 'ORDER BY type DESC, name;'; + case 'alter': + /* + * SQLite does not support ALTER TABLE; this is a helper query + * to handle this. 'table' represents the table name, 'rows' + * the news rows to create, 'save' the row(s) to keep _with_ + * the data. + * + * Use like: + * $args = array( + * 'table' => $table, + * 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT", + * 'save' => "NULL, titel, content, datetime" + * ); + * $res = $db->query( $db->getSpecialQuery('alter', $args)); + */ + $rows = strtr($args['rows'], $this->keywords); + + $q = array( + 'BEGIN TRANSACTION', + "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})", + "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}", + "DROP TABLE {$args['table']}", + "CREATE TABLE {$args['table']} ({$args['rows']})", + "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup", + "DROP TABLE {$args['table']}_backup", + 'COMMIT', + ); + + // This is a dirty hack, since the above query will no get executed with a single + // query call; so here the query method will be called directly and return a select instead. + foreach ($q as $query) { + $this->query($query); + } + return "SELECT * FROM {$args['table']};"; + default: + return null; + } + } + + // }}} + // {{{ getDbFileStats() + + /** + * Get the file stats for the current database. + * + * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size, + * atime, mtime, ctime, blksize, blocks or a numeric key between + * 0 and 12. + * + * @param string $arg Array key for stats() + * @return mixed array on an unspecified key, integer on a passed arg and + * false at a stats error. + */ + function getDbFileStats($arg = '') + { + $stats = stat($this->dsn['database']); + if ($stats == false) { + return false; + } + if (is_array($stats)) { + if (is_numeric($arg)) { + if (((int)$arg <= 12) & ((int)$arg >= 0)) { + return false; + } + return $stats[$arg ]; + } + if (array_key_exists(trim($arg), $stats)) { + return $stats[$arg ]; + } + } + return $stats; + } + + // }}} + // {{{ escapeSimple() + + /** + * Escape a string according to the current DBMS's standards + * + * In SQLite, this makes things safe for inserts/updates, but may + * cause problems when performing text comparisons against columns + * containing binary data. See the + * {@link http://php.net/sqlite_escape_string PHP manual} for more info. + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @since 1.6.1 + * @see DB_common::escapeSimple() + * @internal + */ + function escapeSimple($str) { + return @sqlite_escape_string($str); + } + + // }}} + // {{{ modifyLimitQuery() + + function modifyLimitQuery($query, $from, $count, $params = array()) + { + $query = $query . " LIMIT $count OFFSET $from"; + return $query; + } + + // }}} + // {{{ modifyQuery() + + /** + * "DELETE FROM table" gives 0 affected rows in SQLite. + * + * This little hack lets you know how many rows were deleted. + * + * @param string $query The SQL query string + * @return string The SQL query string + */ + function _modifyQuery($query) + { + if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { + $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', + 'DELETE FROM \1 WHERE 1=1', $query); + } + } + return $query; + } + + // }}} + // {{{ sqliteRaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $errno PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see errorNative() + * @see errorCode() + * @see DB_common::raiseError() + */ + function sqliteRaiseError($errno = null) + { + + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + + $errorcode = @sqlite_last_error($this->connection); + $userinfo = "$errorcode ** $this->last_query"; + + return $this->raiseError($errno, null, null, $userinfo, $native); + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/storage.php b/thirdparty/pear/DB/storage.php new file mode 100644 index 0000000..bc36533 --- /dev/null +++ b/thirdparty/pear/DB/storage.php @@ -0,0 +1,495 @@ + | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once 'DB.php'; + +/** + * Provides an object interface to a table row. + * + * It lets you add, delete and change rows using objects rather than SQL + * statements. + * + * @package DB + * @version $Id$ + * @category Database + * @author Stig Bakken + */ +class DB_storage extends PEAR +{ + // {{{ properties + + /** the name of the table (or view, if the backend database supports + updates in views) we hold data from */ + var $_table = null; + + /** which column(s) in the table contains primary keys, can be a + string for single-column primary keys, or an array of strings + for multiple-column primary keys */ + var $_keycolumn = null; + + /** DB connection handle used for all transactions */ + var $_dbh = null; + + /** an assoc with the names of database fields stored as properties + in this object */ + var $_properties = array(); + + /** an assoc with the names of the properties in this object that + have been changed since they were fetched from the database */ + var $_changes = array(); + + /** flag that decides if data in this object can be changed. + objects that don't have their table's key column in their + property lists will be flagged as read-only. */ + var $_readonly = false; + + /** function or method that implements a validator for fields that + are set, this validator function returns true if the field is + valid, false if not */ + var $_validator = null; + + // }}} + // {{{ constructor + + /** + * Constructor + * + * @param $table string the name of the database table + * + * @param $keycolumn mixed string with name of key column, or array of + * strings if the table has a primary key of more than one column + * + * @param $dbh object database connection object + * + * @param $validator mixed function or method used to validate + * each new value, called with three parameters: the name of the + * field/column that is changing, a reference to the new value and + * a reference to this object + * + */ + function DB_storage($table, $keycolumn, &$dbh, $validator = null) + { + $this->PEAR('DB_Error'); + $this->_table = $table; + $this->_keycolumn = $keycolumn; + $this->_dbh = $dbh; + $this->_readonly = false; + $this->_validator = $validator; + } + + // }}} + // {{{ _makeWhere() + + /** + * Utility method to build a "WHERE" clause to locate ourselves in + * the table. + * + * XXX future improvement: use rowids? + * + * @access private + */ + function _makeWhere($keyval = null) + { + if (is_array($this->_keycolumn)) { + if ($keyval === null) { + for ($i = 0; $i < sizeof($this->_keycolumn); $i++) { + $keyval[] = $this->{$this->_keycolumn[$i]}; + } + } + $whereclause = ''; + for ($i = 0; $i < sizeof($this->_keycolumn); $i++) { + if ($i > 0) { + $whereclause .= ' AND '; + } + $whereclause .= $this->_keycolumn[$i]; + if (is_null($keyval[$i])) { + // there's not much point in having a NULL key, + // but we support it anyway + $whereclause .= ' IS NULL'; + } else { + $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]); + } + } + } else { + if ($keyval === null) { + $keyval = @$this->{$this->_keycolumn}; + } + $whereclause = $this->_keycolumn; + if (is_null($keyval)) { + // there's not much point in having a NULL key, + // but we support it anyway + $whereclause .= ' IS NULL'; + } else { + $whereclause .= ' = ' . $this->_dbh->quote($keyval); + } + } + return $whereclause; + } + + // }}} + // {{{ setup() + + /** + * Method used to initialize a DB_storage object from the + * configured table. + * + * @param $keyval mixed the key[s] of the row to fetch (string or array) + * + * @return int DB_OK on success, a DB error if not + */ + function setup($keyval) + { + $whereclause = $this->_makeWhere($keyval); + $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause; + $sth = $this->_dbh->query($query); + if (DB::isError($sth)) { + return $sth; + } + $row = $sth->fetchRow(DB_FETCHMODE_ASSOC); + if (DB::isError($row)) { + return $row; + } + if (!$row) { + return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null, + $query, null, true); + } + foreach ($row as $key => $value) { + $this->_properties[$key] = true; + $this->$key = $value; + } + return DB_OK; + } + + // }}} + // {{{ insert() + + /** + * Create a new (empty) row in the configured table for this + * object. + */ + function insert($newpk) + { + if (is_array($this->_keycolumn)) { + $primarykey = $this->_keycolumn; + } else { + $primarykey = array($this->_keycolumn); + } + settype($newpk, "array"); + for ($i = 0; $i < sizeof($primarykey); $i++) { + $pkvals[] = $this->_dbh->quote($newpk[$i]); + } + + $sth = $this->_dbh->query("INSERT INTO $this->_table (" . + implode(",", $primarykey) . ") VALUES(" . + implode(",", $pkvals) . ")"); + if (DB::isError($sth)) { + return $sth; + } + if (sizeof($newpk) == 1) { + $newpk = $newpk[0]; + } + $this->setup($newpk); + } + + // }}} + // {{{ toString() + + /** + * Output a simple description of this DB_storage object. + * @return string object description + */ + function toString() + { + $info = strtolower(get_class($this)); + $info .= " (table="; + $info .= $this->_table; + $info .= ", keycolumn="; + if (is_array($this->_keycolumn)) { + $info .= "(" . implode(",", $this->_keycolumn) . ")"; + } else { + $info .= $this->_keycolumn; + } + $info .= ", dbh="; + if (is_object($this->_dbh)) { + $info .= $this->_dbh->toString(); + } else { + $info .= "null"; + } + $info .= ")"; + if (sizeof($this->_properties)) { + $info .= " [loaded, key="; + $keyname = $this->_keycolumn; + if (is_array($keyname)) { + $info .= "("; + for ($i = 0; $i < sizeof($keyname); $i++) { + if ($i > 0) { + $info .= ","; + } + $info .= $this->$keyname[$i]; + } + $info .= ")"; + } else { + $info .= $this->$keyname; + } + $info .= "]"; + } + if (sizeof($this->_changes)) { + $info .= " [modified]"; + } + return $info; + } + + // }}} + // {{{ dump() + + /** + * Dump the contents of this object to "standard output". + */ + function dump() + { + foreach ($this->_properties as $prop => $foo) { + print "$prop = "; + print htmlentities($this->$prop); + print "
\n"; + } + } + + // }}} + // {{{ &create() + + /** + * Static method used to create new DB storage objects. + * @param $data assoc. array where the keys are the names + * of properties/columns + * @return object a new instance of DB_storage or a subclass of it + */ + function &create($table, &$data) + { + $classname = strtolower(get_class($this)); + $obj =& new $classname($table); + foreach ($data as $name => $value) { + $obj->_properties[$name] = true; + $obj->$name = &$value; + } + return $obj; + } + + // }}} + // {{{ loadFromQuery() + + /** + * Loads data into this object from the given query. If this + * object already contains table data, changes will be saved and + * the object re-initialized first. + * + * @param $query SQL query + * + * @param $params parameter list in case you want to use + * prepare/execute mode + * + * @return int DB_OK on success, DB_WARNING_READ_ONLY if the + * returned object is read-only (because the object's specified + * key column was not found among the columns returned by $query), + * or another DB error code in case of errors. + */ +// XXX commented out for now +/* + function loadFromQuery($query, $params = null) + { + if (sizeof($this->_properties)) { + if (sizeof($this->_changes)) { + $this->store(); + $this->_changes = array(); + } + $this->_properties = array(); + } + $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params); + if (DB::isError($rowdata)) { + return $rowdata; + } + reset($rowdata); + $found_keycolumn = false; + while (list($key, $value) = each($rowdata)) { + if ($key == $this->_keycolumn) { + $found_keycolumn = true; + } + $this->_properties[$key] = true; + $this->$key = &$value; + unset($value); // have to unset, or all properties will + // refer to the same value + } + if (!$found_keycolumn) { + $this->_readonly = true; + return DB_WARNING_READ_ONLY; + } + return DB_OK; + } + */ + + // }}} + // {{{ set() + + /** + * Modify an attriute value. + */ + function set($property, $newvalue) + { + // only change if $property is known and object is not + // read-only + if ($this->_readonly) { + return $this->raiseError(null, DB_WARNING_READ_ONLY, null, + null, null, null, true); + } + if (@isset($this->_properties[$property])) { + if (empty($this->_validator)) { + $valid = true; + } else { + $valid = @call_user_func($this->_validator, + $this->_table, + $property, + $newvalue, + $this->$property, + $this); + } + if ($valid) { + $this->$property = $newvalue; + if (empty($this->_changes[$property])) { + $this->_changes[$property] = 0; + } else { + $this->_changes[$property]++; + } + } else { + return $this->raiseError(null, DB_ERROR_INVALID, null, + null, "invalid field: $property", + null, true); + } + return true; + } + return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null, + null, "unknown field: $property", + null, true); + } + + // }}} + // {{{ &get() + + /** + * Fetch an attribute value. + * + * @param string attribute name + * + * @return attribute contents, or null if the attribute name is + * unknown + */ + function &get($property) + { + // only return if $property is known + if (isset($this->_properties[$property])) { + return $this->$property; + } + $tmp = null; + return $tmp; + } + + // }}} + // {{{ _DB_storage() + + /** + * Destructor, calls DB_storage::store() if there are changes + * that are to be kept. + */ + function _DB_storage() + { + if (sizeof($this->_changes)) { + $this->store(); + } + $this->_properties = array(); + $this->_changes = array(); + $this->_table = null; + } + + // }}} + // {{{ store() + + /** + * Stores changes to this object in the database. + * + * @return DB_OK or a DB error + */ + function store() + { + foreach ($this->_changes as $name => $foo) { + $params[] = &$this->$name; + $vars[] = $name . ' = ?'; + } + if ($vars) { + $query = 'UPDATE ' . $this->_table . ' SET ' . + implode(', ', $vars) . ' WHERE ' . + $this->_makeWhere(); + $stmt = $this->_dbh->prepare($query); + $res = $this->_dbh->execute($stmt, $params); + if (DB::isError($res)) { + return $res; + } + $this->_changes = array(); + } + return DB_OK; + } + + // }}} + // {{{ remove() + + /** + * Remove the row represented by this object from the database. + * + * @return mixed DB_OK or a DB error + */ + function remove() + { + if ($this->_readonly) { + return $this->raiseError(null, DB_WARNING_READ_ONLY, null, + null, null, null, true); + } + $query = 'DELETE FROM ' . $this->_table .' WHERE '. + $this->_makeWhere(); + $res = $this->_dbh->query($query); + if (DB::isError($res)) { + return $res; + } + foreach ($this->_properties as $prop => $foo) { + unset($this->$prop); + } + $this->_properties = array(); + $this->_changes = array(); + return DB_OK; + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/DB/sybase.php b/thirdparty/pear/DB/sybase.php new file mode 100644 index 0000000..93e4e79 --- /dev/null +++ b/thirdparty/pear/DB/sybase.php @@ -0,0 +1,835 @@ + | +// | Antônio Carlos Venâncio Júnior | +// | Maintainer: Daniel Convissor | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +// TODO +// - This driver may fail with multiple connections under the same +// user/pass/host and different databases + + +require_once 'DB/common.php'; + +/** + * Database independent query interface definition for PHP's Sybase + * extension. + * + * @package DB + * @version $Id$ + * @category Database + * @author Sterling Hughes + * @author Antônio Carlos Venâncio Júnior + */ +class DB_sybase extends DB_common +{ + // {{{ properties + + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); + var $transaction_opcount = 0; + var $autocommit = true; + + // }}} + // {{{ constructor + + /** + * DB_sybase constructor. + * + * @access public + */ + function DB_sybase() + { + $this->DB_common(); + $this->phptype = 'sybase'; + $this->dbsyntax = 'sybase'; + $this->features = array( + 'prepare' => false, + 'pconnect' => true, + 'transactions' => false, + 'limit' => 'emulate' + ); + $this->errorcode_map = array( + ); + } + + // }}} + // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param $persistent (optional) whether the connection should + * be persistent + * @access public + * @return int DB_OK on success, a DB error on failure + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('sybase') && + !DB::assertExtension('sybase_ct')) + { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsninfo; + + $interface = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; + $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect'; + $dsninfo['password'] = !empty($dsninfo['password']) ? $dsninfo['password'] : false; + $dsninfo['charset'] = isset($dsninfo['charset']) ? $dsninfo['charset'] : false; + $dsninfo['appname'] = isset($dsninfo['appname']) ? $dsninfo['appname'] : false; + + if ($interface && $dsninfo['username']) { + $conn = @$connect_function($interface, $dsninfo['username'], + $dsninfo['password'], + $dsninfo['charset'], + $dsninfo['appname']); + } else { + $conn = false; + } + + if (!$conn) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED); + } + + if ($dsninfo['database']) { + if (!@sybase_select_db($dsninfo['database'], $conn)) { + return $this->raiseError(DB_ERROR_NODBSELECTED, null, + null, null, @sybase_get_last_message()); + } + $this->_db = $dsninfo['database']; + } + + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Log out and disconnect from the database. + * + * @access public + * + * @return bool true on success, false if not connected. + */ + function disconnect() + { + $ret = @sybase_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ errorNative() + + /** + * Get the last server error messge (if any) + * + * @return string sybase last error message + */ + function errorNative() + { + return @sybase_get_last_message(); + } + + // }}} + // {{{ errorCode() + + /** + * Determine PEAR::DB error code from the database's text error message. + * + * @param string $errormsg error message returned from the database + * @return integer an error number from a DB error constant + */ + function errorCode($errormsg) + { + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/Incorrect syntax near/' + => DB_ERROR_SYNTAX, + '/^Unclosed quote before the character string [\"\'].*[\"\']\./' + => DB_ERROR_SYNTAX, + '/Implicit conversion from datatype [\"\'].+[\"\'] to [\"\'].+[\"\'] is not allowed\./' + => DB_ERROR_INVALID_NUMBER, + '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./' + => DB_ERROR_NOSUCHTABLE, + '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./' + => DB_ERROR_ACCESS_VIOLATION, + '/^.+ permission denied on object .+, database .+, owner .+/' + => DB_ERROR_ACCESS_VIOLATION, + '/^.* permission denied, database .+, owner .+/' + => DB_ERROR_ACCESS_VIOLATION, + '/[^.*] not found\./' + => DB_ERROR_NOSUCHTABLE, + '/There is already an object named/' + => DB_ERROR_ALREADY_EXISTS, + '/Invalid column name/' + => DB_ERROR_NOSUCHFIELD, + '/does not allow null values/' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/Command has been aborted/' + => DB_ERROR_CONSTRAINT, + ); + } + + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} + // {{{ sybaseRaiseError() + + /** + * Gather information about an error, then use that info to create a + * DB error object and finally return that object. + * + * @param integer $errno PEAR error number (usually a DB constant) if + * manually raising an error + * @return object DB error object + * @see errorNative() + * @see errorCode() + * @see DB_common::raiseError() + */ + function sybaseRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + return $this->raiseError($errno, null, null, null, $native); + } + + // }}} + // {{{ simpleQuery() + + /** + * Send a query to Sybase and return the results as a Sybase resource + * identifier. + * + * @param the SQL query + * + * @access public + * + * @return mixed returns a valid Sybase result for successful SELECT + * queries, DB_OK for other successful queries. A DB error is + * returned on failure. + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $query = $this->modifyQuery($query); + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @sybase_query('BEGIN TRANSACTION', $this->connection); + if (!$result) { + return $this->sybaseRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @sybase_query($query, $this->connection); + if (!$result) { + return $this->sybaseRaiseError(); + } + if (is_resource($result)) { + $numrows = $this->numRows($result); + if (is_object($numrows)) { + return $numrows; + } + $this->num_rows[(int)$result] = $numrows; + return $result; + } + // Determine which queries that should return data, and which + // should return an error code only. + return $ismanip ? DB_OK : $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal sybase result pointer to the next available result + * + * @param a valid sybase result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * @param resource $result query result identifier + * @param array $arr (reference) array where data from the row + * should be placed + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch + * + * @return mixed DB_OK on success, null when end of result set is + * reached or on failure + * + * @see DB_result::fetchInto() + * @access private + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + if (!@sybase_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + if (function_exists('sybase_fetch_assoc')) { + $arr = @sybase_fetch_assoc($result); + } else { + if ($arr = @sybase_fetch_array($result)) { + foreach ($arr as $key => $value) { + if (is_int($key)) { + unset($arr[$key]); + } + } + } + } + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @sybase_fetch_row($result); + } + if (!$arr) { + // reported not work as seems that sybase_get_last_message() + // always return a message here + //if ($errmsg = @sybase_get_last_message()) { + // return $this->sybaseRaiseError($errmsg); + //} else { + return null; + //} + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Free the internal resources associated with $result. + * + * @param $result Sybase result identifier + * + * @access public + * + * @return bool true on success, false if $result is invalid + */ + function freeResult($result) + { + unset($this->num_rows[(int)$result]); + return @sybase_free_result($result); + } + + // }}} + // {{{ numCols() + + /** + * Get the number of columns in a result set. + * + * @param $result Sybase result identifier + * + * @access public + * + * @return int the number of columns per row in $result + */ + function numCols($result) + { + $cols = @sybase_num_fields($result); + if (!$cols) { + return $this->sybaseRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Get the number of rows in a result set. + * + * @param $result Sybase result identifier + * + * @access public + * + * @return int the number of rows in $result + */ + function numRows($result) + { + $rows = @sybase_num_rows($result); + if ($rows === false) { + return $this->sybaseRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affectedRows() + + /** + * Gets the number of rows affected by the data manipulation + * query. For other queries, this function returns 0. + * + * @return number of rows affected by the last query + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + $result = @sybase_affected_rows($this->connection); + } else { + $result = 0; + } + return $result; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. DB_Error if problem. + * + * @internal + * @see DB_common::nextID() + * @access public + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) + { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } elseif (!DB::isError($result)) { + $result =& $this->query("SELECT @@IDENTITY FROM $seqname"); + $repeat = 0; + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $result = $result->fetchRow(DB_FETCHMODE_ORDERED); + return $result[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object is returned if + * problems arise. + * + * @internal + * @see DB_common::createSequence() + * @access public + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("CREATE TABLE $seqname ". + '(id numeric(10,0) IDENTITY NOT NULL ,' . + 'vapor int NULL)'); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. DB_Error if problems. + * + * @internal + * @see DB_common::dropSequence() + * @access public + */ + function dropSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + return $this->query("DROP TABLE $seqname"); + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Returns the query needed to get some backend info + * @param string $type What kind of info you want to retrieve + * @return string The SQL query string + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return "select name from sysobjects where type = 'U' order by name"; + case 'views': + return "select name from sysobjects where type = 'V'"; + default: + return null; + } + } + + // }}} + // {{{ autoCommit() + + /** + * Enable/disable automatic commits + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commit the current transaction. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @sybase_query('COMMIT', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->sybaseRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Roll back (undo) the current transaction. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @sybase_query('ROLLBACK', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->sybaseRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set. + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table + * @param int $mode a valid tableInfo mode + * @return array an associative array with the information requested + * or an error object if something is wrong + * @access public + * @internal + * @since 1.6.0 + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } elseif (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $id = @sybase_query("SELECT * FROM $result WHERE 1=0", + $this->connection); + $got_string = true; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @sybase_num_fields($id); + + // made this IF due to performance (one if is faster than $count if's) + if (!$mode) { + + for ($i=0; $i<$count; $i++) { + $f = @sybase_fetch_field($id, $i); + + // column_source is often blank + if ($got_string) { + $res[$i]['table'] = $case_func($result); + } else { + $res[$i]['table'] = $case_func($f->column_source); + } + $res[$i]['name'] = $case_func($f->name); + $res[$i]['type'] = $f->type; + $res[$i]['len'] = $f->max_length; + if ($res[$i]['table']) { + $res[$i]['flags'] = $this->_sybase_field_flags( + $res[$i]['table'], $res[$i]['name']); + } else { + $res[$i]['flags'] = ''; + } + } + + } else { + // get full info + + $res['num_fields'] = $count; + + for ($i=0; $i<$count; $i++) { + $f = @sybase_fetch_field($id, $i); + + // column_source is often blank + if ($got_string) { + $res[$i]['table'] = $case_func($result); + } else { + $res[$i]['table'] = $case_func($f->column_source); + } + $res[$i]['name'] = $case_func($f->name); + $res[$i]['type'] = $f->type; + $res[$i]['len'] = $f->max_length; + if ($res[$i]['table']) { + $res[$i]['flags'] = $this->_sybase_field_flags( + $res[$i]['table'], $res[$i]['name']); + } else { + $res[$i]['flags'] = ''; + } + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + } + + // free the result only if we were called on a table + if ($got_string) { + @sybase_free_result($id); + } + return $res; + } + + // }}} + // {{{ _sybase_field_flags() + + /** + * Get the flags for a field. + * + * Currently supports: + * + unique_key (unique index, unique check or primary_key) + * + multiple_key (multi-key index) + * + * @param string $table table name + * @param string $column field name + * @return string space delimited string of flags. Empty string if none. + * @access private + */ + function _sybase_field_flags($table, $column) + { + static $tableName = null; + static $flags = array(); + + if ($table != $tableName) { + $flags = array(); + $tableName = $table; + + // get unique/primary keys + $res = $this->getAll("sp_helpindex $table", DB_FETCHMODE_ASSOC); + + if (!isset($res[0]['index_description'])) { + return ''; + } + + foreach ($res as $val) { + $keys = explode(', ', trim($val['index_keys'])); + + if (sizeof($keys) > 1) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'multiple_key'); + } + } + + if (strpos($val['index_description'], 'unique')) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'unique_key'); + } + } + } + + } + + if (array_key_exists($column, $flags)) { + return(implode(' ', $flags[$column])); + } + + return ''; + } + + // }}} + // {{{ _add_flag() + + /** + * Adds a string to the flags array if the flag is not yet in there + * - if there is no flag present the array is created. + * + * @param array $array reference of flags array to add a value to + * @param mixed $value value to add to the flag array + * @access private + */ + function _add_flag(&$array, $value) + { + if (!is_array($array)) { + $array = array($value); + } elseif (!in_array($value, $array)) { + array_push($array, $value); + } + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quote a string so it can be safely used as a table / column name + * + * Quoting style depends on which database driver is being used. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @since 1.6.0 + * @access public + */ + function quoteIdentifier($str) + { + return '[' . str_replace(']', ']]', $str) . ']'; + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/thirdparty/pear/Log.php b/thirdparty/pear/Log.php new file mode 100644 index 0000000..a762882 --- /dev/null +++ b/thirdparty/pear/Log.php @@ -0,0 +1,635 @@ + + * @author Jon Parise + * @since Horde 1.3 + * @package Log + */ +class Log +{ + /** + * Indicates whether or not the log can been opened / connected. + * + * @var boolean + * @access private + */ + var $_opened = false; + + /** + * Instance-specific unique identification number. + * + * @var integer + * @access private + */ + var $_id = 0; + + /** + * The label that uniquely identifies this set of log messages. + * + * @var string + * @access private + */ + var $_ident = ''; + + /** + * The default priority to use when logging an event. + * + * @var integer + * @access private + */ + var $_priority = PEAR_LOG_INFO; + + /** + * The bitmask of allowed log levels. + * @var integer + * @access private + */ + var $_mask = PEAR_LOG_ALL; + + /** + * Holds all Log_observer objects that wish to be notified of new messages. + * + * @var array + * @access private + */ + var $_listeners = array(); + + + /** + * Attempts to return a concrete Log instance of type $handler. + * + * @param string $handler The type of concrete Log subclass to return. + * Attempt to dynamically include the code for + * this subclass. Currently, valid values are + * 'console', 'syslog', 'sql', 'file', and 'mcal'. + * + * @param string $name The name of the actually log file, table, or + * other specific store to use. Defaults to an + * empty string, with which the subclass will + * attempt to do something intelligent. + * + * @param string $ident The identity reported to the log system. + * + * @param array $conf A hash containing any additional configuration + * information that a subclass might need. + * + * @param int $level Log messages up to and including this level. + * + * @return object Log The newly created concrete Log instance, or an + * false on an error. + * @access public + * @since Log 1.0 + */ + function &factory($handler, $name = '', $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $handler = strtolower($handler); + $class = 'Log_' . $handler; + $classfile = 'Log/' . $handler . '.php'; + + /* + * Attempt to include our version of the named class, but don't treat + * a failure as fatal. The caller may have already included their own + * version of the named class. + */ + @include_once $classfile; + + /* If the class exists, return a new instance of it. */ + if (class_exists($class)) { + return new $class($name, $ident, $conf, $level); + } + + return false; + } + + /** + * Attempts to return a reference to a concrete Log instance of type + * $handler, only creating a new instance if no log instance with the same + * parameters currently exists. + * + * You should use this if there are multiple places you might create a + * logger, you don't want to create multiple loggers, and you don't want to + * check for the existance of one each time. The singleton pattern does all + * the checking work for you. + * + * You MUST call this method with the $var = &Log::singleton() syntax. + * Without the ampersand (&) in front of the method name, you will not get + * a reference, you will get a copy. + * + * @param string $handler The type of concrete Log subclass to return. + * Attempt to dynamically include the code for + * this subclass. Currently, valid values are + * 'console', 'syslog', 'sql', 'file', and 'mcal'. + * + * @param string $name The name of the actually log file, table, or + * other specific store to use. Defaults to an + * empty string, with which the subclass will + * attempt to do something intelligent. + * + * @param string $ident The identity reported to the log system. + * + * @param array $conf A hash containing any additional configuration + * information that a subclass might need. + * + * @param int $level Log messages up to and including this level. + * + * @return object Log The newly created concrete Log instance, or an + * false on an error. + * @access public + * @since Log 1.0 + */ + function &singleton($handler, $name = '', $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + static $instances; + if (!isset($instances)) $instances = array(); + + $signature = serialize(array($handler, $name, $ident, $conf, $level)); + if (!isset($instances[$signature])) { + $instances[$signature] = &Log::factory($handler, $name, $ident, + $conf, $level); + } + + return $instances[$signature]; + } + + /** + * Abstract implementation of the open() method. + * @since Log 1.0 + */ + function open() + { + return false; + } + + /** + * Abstract implementation of the close() method. + * @since Log 1.0 + */ + function close() + { + return false; + } + + /** + * Abstract implementation of the flush() method. + * @since Log 1.8.2 + */ + function flush() + { + return false; + } + + /** + * Abstract implementation of the log() method. + * @since Log 1.0 + */ + function log($message, $priority = null) + { + return false; + } + + /** + * A convenience function for logging a emergency event. It will log a + * message at the PEAR_LOG_EMERG log level. + * + * @param mixed $message String or object containing the message + * to log. + * + * @return boolean True if the message was successfully logged. + * + * @access public + * @since Log 1.7.0 + */ + function emerg($message) + { + return $this->log($message, PEAR_LOG_EMERG); + } + + /** + * A convenience function for logging an alert event. It will log a + * message at the PEAR_LOG_ALERT log level. + * + * @param mixed $message String or object containing the message + * to log. + * + * @return boolean True if the message was successfully logged. + * + * @access public + * @since Log 1.7.0 + */ + function alert($message) + { + return $this->log($message, PEAR_LOG_ALERT); + } + + /** + * A convenience function for logging a critical event. It will log a + * message at the PEAR_LOG_CRIT log level. + * + * @param mixed $message String or object containing the message + * to log. + * + * @return boolean True if the message was successfully logged. + * + * @access public + * @since Log 1.7.0 + */ + function crit($message) + { + return $this->log($message, PEAR_LOG_CRIT); + } + + /** + * A convenience function for logging a error event. It will log a + * message at the PEAR_LOG_ERR log level. + * + * @param mixed $message String or object containing the message + * to log. + * + * @return boolean True if the message was successfully logged. + * + * @access public + * @since Log 1.7.0 + */ + function err($message) + { + return $this->log($message, PEAR_LOG_ERR); + } + + /** + * A convenience function for logging a warning event. It will log a + * message at the PEAR_LOG_WARNING log level. + * + * @param mixed $message String or object containing the message + * to log. + * + * @return boolean True if the message was successfully logged. + * + * @access public + * @since Log 1.7.0 + */ + function warning($message) + { + return $this->log($message, PEAR_LOG_WARNING); + } + + /** + * A convenience function for logging a notice event. It will log a + * message at the PEAR_LOG_NOTICE log level. + * + * @param mixed $message String or object containing the message + * to log. + * + * @return boolean True if the message was successfully logged. + * + * @access public + * @since Log 1.7.0 + */ + function notice($message) + { + return $this->log($message, PEAR_LOG_NOTICE); + } + + /** + * A convenience function for logging a information event. It will log a + * message at the PEAR_LOG_INFO log level. + * + * @param mixed $message String or object containing the message + * to log. + * + * @return boolean True if the message was successfully logged. + * + * @access public + * @since Log 1.7.0 + */ + function info($message) + { + return $this->log($message, PEAR_LOG_INFO); + } + + /** + * A convenience function for logging a debug event. It will log a + * message at the PEAR_LOG_DEBUG log level. + * + * @param mixed $message String or object containing the message + * to log. + * + * @return boolean True if the message was successfully logged. + * + * @access public + * @since Log 1.7.0 + */ + function debug($message) + { + return $this->log($message, PEAR_LOG_DEBUG); + } + + /** + * Returns the string representation of the message data. + * + * If $message is an object, _extractMessage() will attempt to extract + * the message text using a known method (such as a PEAR_Error object's + * getMessage() method). If a known method, cannot be found, the + * serialized representation of the object will be returned. + * + * If the message data is already a string, it will be returned unchanged. + * + * @param mixed $message The original message data. This may be a + * string or any object. + * + * @return string The string representation of the message. + * + * @access private + */ + function _extractMessage($message) + { + /* + * If we've been given an object, attempt to extract the message using + * a known method. If we can't find such a method, default to the + * "human-readable" version of the object. + * + * We also use the human-readable format for arrays. + */ + if (is_object($message)) { + if (method_exists($message, 'getmessage')) { + $message = $message->getMessage(); + } else if (method_exists($message, 'tostring')) { + $message = $message->toString(); + } else if (method_exists($message, '__tostring')) { + $message = (string)$message; + } else { + $message = print_r($message, true); + } + } else if (is_array($message)) { + if (isset($message['message'])) { + $message = $message['message']; + } else { + $message = print_r($message, true); + } + } + + /* Otherwise, we assume the message is a string. */ + return $message; + } + + /** + * Returns the string representation of a PEAR_LOG_* integer constant. + * + * @param int $priority A PEAR_LOG_* integer constant. + * + * @return string The string representation of $level. + * + * @since Log 1.0 + */ + function priorityToString($priority) + { + $levels = array( + PEAR_LOG_EMERG => 'emergency', + PEAR_LOG_ALERT => 'alert', + PEAR_LOG_CRIT => 'critical', + PEAR_LOG_ERR => 'error', + PEAR_LOG_WARNING => 'warning', + PEAR_LOG_NOTICE => 'notice', + PEAR_LOG_INFO => 'info', + PEAR_LOG_DEBUG => 'debug' + ); + + return $levels[$priority]; + } + + /** + * Calculate the log mask for the given priority. + * + * @param integer $priority The priority whose mask will be calculated. + * + * @return integer The calculated log mask. + * + * @access public + * @since Log 1.7.0 + */ + function MASK($priority) + { + return (1 << $priority); + } + + /** + * Calculate the log mask for all priorities up to the given priority. + * + * @param integer $priority The maximum priority covered by this mask. + * + * @return integer The calculated log mask. + * + * @access public + * @since Log 1.7.0 + */ + function UPTO($priority) + { + return ((1 << ($priority + 1)) - 1); + } + + /** + * Set and return the level mask for the current Log instance. + * + * @param integer $mask A bitwise mask of log levels. + * + * @return integer The current level mask. + * + * @access public + * @since Log 1.7.0 + */ + function setMask($mask) + { + $this->_mask = $mask; + + return $this->_mask; + } + + /** + * Returns the current level mask. + * + * @return interger The current level mask. + * + * @access public + * @since Log 1.7.0 + */ + function getMask() + { + return $this->_mask; + } + + /** + * Check if the given priority is included in the current level mask. + * + * @param integer $priority The priority to check. + * + * @return boolean True if the given priority is included in the current + * log mask. + * + * @access private + * @since Log 1.7.0 + */ + function _isMasked($priority) + { + return (Log::MASK($priority) & $this->_mask); + } + + /** + * Returns the current default priority. + * + * @return integer The current default priority. + * + * @access public + * @since Log 1.8.4 + */ + function getPriority() + { + return $this->_priority; + } + + /** + * Sets the default priority to the specified value. + * + * @param integer $priority The new default priority. + * + * @access public + * @since Log 1.8.4 + */ + function setPriority($priority) + { + $this->_priority = $priority; + } + + /** + * Adds a Log_observer instance to the list of observers that are listening + * for messages emitted by this Log instance. + * + * @param object $observer The Log_observer instance to attach as a + * listener. + * + * @param boolean True if the observer is successfully attached. + * + * @access public + * @since Log 1.0 + */ + function attach(&$observer) + { + if (!is_a($observer, 'Log_observer')) { + return false; + } + + $this->_listeners[$observer->_id] = &$observer; + + return true; + } + + /** + * Removes a Log_observer instance from the list of observers. + * + * @param object $observer The Log_observer instance to detach from + * the list of listeners. + * + * @param boolean True if the observer is successfully detached. + * + * @access public + * @since Log 1.0 + */ + function detach($observer) + { + if (!is_a($observer, 'Log_observer') || + !isset($this->_listeners[$observer->_id])) { + return false; + } + + unset($this->_listeners[$observer->_id]); + + return true; + } + + /** + * Informs each registered observer instance that a new message has been + * logged. + * + * @param array $event A hash describing the log event. + * + * @access private + */ + function _announce($event) + { + foreach ($this->_listeners as $id => $listener) { + if ($event['priority'] <= $this->_listeners[$id]->_priority) { + $this->_listeners[$id]->notify($event); + } + } + } + + /** + * Indicates whether this is a composite class. + * + * @return boolean True if this is a composite class. + * + * @access public + * @since Log 1.0 + */ + function isComposite() + { + return false; + } + + /** + * Sets this Log instance's identification string. + * + * @param string $ident The new identification string. + * + * @access public + * @since Log 1.6.3 + */ + function setIdent($ident) + { + $this->_ident = $ident; + } + + /** + * Returns the current identification string. + * + * @return string The current Log instance's identification string. + * + * @access public + * @since Log 1.6.3 + */ + function getIdent() + { + return $this->_ident; + } +} + +?> diff --git a/thirdparty/pear/Log/composite.php b/thirdparty/pear/Log/composite.php new file mode 100644 index 0000000..3b0726c --- /dev/null +++ b/thirdparty/pear/Log/composite.php @@ -0,0 +1,196 @@ + + * @author Jon Parise + * + * @since Horde 1.3 + * @since Log 1.0 + * @package Log + * + * @example composite.php Using the composite handler. + */ +class Log_composite extends Log +{ + /** + * Array holding all of the Log instances to which log events should be + * sent. + * + * @var array + * @access private + */ + var $_children = array(); + + + /** + * Constructs a new composite Log object. + * + * @param boolean $name This parameter is ignored. + * @param boolean $ident This parameter is ignored. + * @param boolean $conf This parameter is ignored. + * @param boolean $level This parameter is ignored. + * + * @access public + */ + function Log_composite($name = false, $ident = false, $conf = false, + $level = PEAR_LOG_DEBUG) + { + } + + /** + * Opens the child connections. + * + * @access public + */ + function open() + { + if (!$this->_opened) { + foreach ($this->_children as $id => $child) { + $this->_children[$id]->open(); + } + $this->_opened = true; + } + } + + /** + * Closes any child instances. + * + * @access public + */ + function close() + { + if ($this->_opened) { + foreach ($this->_children as $id => $child) { + $this->_children[$id]->close(); + } + $this->_opened = false; + } + } + + /** + * Flushes all open child instances. + * + * @access public + * @since Log 1.8.2 + */ + function flush() + { + if ($this->_opened) { + foreach ($this->_children as $id => $child) { + $this->_children[$id]->flush(); + } + } + } + + /** + * Sends $message and $priority to each child of this composite. + * + * @param mixed $message String or object containing the message + * to log. + * @param string $priority (optional) The priority of the message. + * Valid values are: PEAR_LOG_EMERG, + * PEAR_LOG_ALERT, PEAR_LOG_CRIT, + * PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and + * PEAR_LOG_DEBUG. + * + * @return boolean True if the entry is successfully logged. + * + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + foreach ($this->_children as $id => $child) { + $this->_children[$id]->log($message, $priority); + } + + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } + + /** + * Returns true if this is a composite. + * + * @return boolean True if this is a composite class. + * + * @access public + */ + function isComposite() + { + return true; + } + + /** + * Sets this identification string for all of this composite's children. + * + * @param string $ident The new identification string. + * + * @access public + * @since Log 1.6.7 + */ + function setIdent($ident) + { + foreach ($this->_children as $id => $child) { + $this->_children[$id]->setIdent($ident); + } + } + + /** + * Adds a Log instance to the list of children. + * + * @param object $child The Log instance to add. + * + * @return boolean True if the Log instance was successfully added. + * + * @access public + */ + function addChild(&$child) + { + /* Make sure this is a Log instance. */ + if (!is_a($child, 'Log')) { + return false; + } + + $this->_children[$child->_id] = &$child; + + return true; + } + + /** + * Removes a Log instance from the list of children. + * + * @param object $child The Log instance to remove. + * + * @return boolean True if the Log instance was successfully removed. + * + * @access public + */ + function removeChild($child) + { + if (!is_a($child, 'Log') || !isset($this->_children[$child->_id])) { + return false; + } + + unset($this->_children[$child->_id]); + + return true; + } +} + +?> diff --git a/thirdparty/pear/Log/console.php b/thirdparty/pear/Log/console.php new file mode 100644 index 0000000..01c4f84 --- /dev/null +++ b/thirdparty/pear/Log/console.php @@ -0,0 +1,190 @@ + + * @since Log 1.1 + * @package Log + * + * @example console.php Using the console handler. + */ +class Log_console extends Log +{ + /** + * Handle to the current output stream. + * @var resource + * @access private + */ + var $_stream = STDOUT; + + /** + * Should the output be buffered or displayed immediately? + * @var string + * @access private + */ + var $_buffering = false; + + /** + * String holding the buffered output. + * @var string + * @access private + */ + var $_buffer = ''; + + /** + * String containing the format of a log line. + * @var string + * @access private + */ + var $_lineFormat = '%1$s %2$s [%3$s] %4$s'; + + /** + * String containing the timestamp format. It will be passed directly to + * strftime(). Note that the timestamp string will generated using the + * current locale. + * @var string + * @access private + */ + var $_timeFormat = '%b %d %H:%M:%S'; + + /** + * Hash that maps canonical format keys to position arguments for the + * "line format" string. + * @var array + * @access private + */ + var $_formatMap = array('%{timestamp}' => '%1$s', + '%{ident}' => '%2$s', + '%{priority}' => '%3$s', + '%{message}' => '%4$s', + '%\{' => '%%{'); + + /** + * Constructs a new Log_console object. + * + * @param string $name Ignored. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_console($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + + if (!empty($conf['stream'])) { + $this->_stream = $conf['stream']; + } + + if (isset($conf['buffering'])) { + $this->_buffering = $conf['buffering']; + } + + if (!empty($conf['lineFormat'])) { + $this->_lineFormat = str_replace(array_keys($this->_formatMap), + array_values($this->_formatMap), + $conf['lineFormat']); + } + + if (!empty($conf['timeFormat'])) { + $this->_timeFormat = $conf['timeFormat']; + } + + /* + * If output buffering has been requested, we need to register a + * shutdown function that will dump the buffer upon termination. + */ + if ($this->_buffering) { + register_shutdown_function(array(&$this, '_Log_console')); + } + } + + /** + * Destructor + */ + function _Log_console() + { + $this->flush(); + } + + /** + * Flushes all pending ("buffered") data to the output stream. + * + * @access public + * @since Log 1.8.2 + */ + function flush() + { + /* + * If output buffering is enabled, dump the contents of the buffer to + * the output stream. + */ + if ($this->_buffering && (strlen($this->_buffer) > 0)) { + fwrite($this->_stream, $this->_buffer); + $this->_buffer = ''; + } + + return fflush($this->_stream); + } + + /** + * Writes $message to the text console. Also, passes the message + * along to any Log_observer instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + /* Build the string containing the complete log line. */ + $line = sprintf($this->_lineFormat, strftime($this->_timeFormat), + $this->_ident, $this->priorityToString($priority), + $message) . "\n"; + + /* + * If buffering is enabled, append this line to the output buffer. + * Otherwise, print the line to the output stream immediately. + */ + if ($this->_buffering) { + $this->_buffer .= $line; + } else { + fwrite($this->_stream, $line); + } + + /* Notify observers about this log message. */ + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } +} + +?> diff --git a/thirdparty/pear/Log/daemon.php b/thirdparty/pear/Log/daemon.php new file mode 100644 index 0000000..5a81706 --- /dev/null +++ b/thirdparty/pear/Log/daemon.php @@ -0,0 +1,229 @@ + + * @version $Revision$ + * @package Log + */ +class Log_daemon extends Log { + + /** + * Integer holding the log facility to use. + * @var string + */ + var $_name = LOG_DAEMON; + + /** + * Var holding the resource pointer to the socket + * @var resource + */ + var $_socket; + + /** + * The ip address or servername + * @see http://www.php.net/manual/en/transports.php + * @var string + */ + var $_ip = '127.0.0.1'; + + /** + * Protocol to use (tcp, udp, etc.) + * @see http://www.php.net/manual/en/transports.php + * @var string + */ + var $_proto = 'udp'; + + /** + * Port to connect to + * @var int + */ + var $_port = 514; + + /** + * Maximum message length in bytes + * @var int + */ + var $_maxsize = 4096; + + /** + * Socket timeout in seconds + * @var int + */ + var $_timeout = 1; + + + /** + * Constructs a new syslog object. + * + * @param string $name The syslog facility. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $maxLevel Maximum level at which to log. + * @access public + */ + function Log_daemon($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + /* Ensure we have a valid integer value for $name. */ + if (empty($name) || !is_int($name)) { + $name = LOG_SYSLOG; + } + + $this->_id = md5(microtime()); + $this->_name = $name; + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + + if (isset($conf['ip'])) { + $this->_ip = $conf['ip']; + } + if (isset($conf['proto'])) { + $this->_proto = $conf['proto']; + } + if (isset($conf['port'])) { + $this->_port = $conf['port']; + } + if (isset($conf['maxsize'])) { + $this->_maxsize = $conf['maxsize']; + } + if (isset($conf['timeout'])) { + $this->_timeout = $conf['timeout']; + } + $this->_proto = $this->_proto . '://'; + + register_shutdown_function(array(&$this, '_Log_daemon')); + } + + /** + * Destructor. + * + * @access private + */ + function _Log_daemon() + { + $this->close(); + } + + /** + * Opens a connection to the system logger, if it has not already + * been opened. This is implicitly called by log(), if necessary. + * @access public + */ + function open() + { + if (!$this->_opened) { + $this->_opened = (bool)($this->_socket = @fsockopen( + $this->_proto . $this->_ip, + $this->_port, + $errno, + $errstr, + $this->_timeout)); + } + return $this->_opened; + } + + /** + * Closes the connection to the system logger, if it is open. + * @access public + */ + function close() + { + if ($this->_opened) { + $this->_opened = false; + return fclose($this->_socket); + } + return true; + } + + /** + * Sends $message to the currently open syslog connection. Calls + * open() if necessary. Also passes the message along to any Log_observer + * instances that are observing this Log. + * + * @param string $message The textual message to be logged. + * @param int $priority (optional) The priority of the message. Valid + * values are: LOG_EMERG, LOG_ALERT, LOG_CRIT, + * LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, + * and LOG_DEBUG. The default is LOG_INFO. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* If the connection isn't open and can't be opened, return failure. */ + if (!$this->_opened && !$this->open()) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + /* Set the facility level. */ + $facility_level = intval($this->_name) + + intval($this->_toSyslog($priority)); + + /* Prepend ident info. */ + if (!empty($this->_ident)) { + $message = $this->_ident . ' ' . $message; + } + + /* Check for message length. */ + if (strlen($message) > $this->_maxsize) { + $message = substr($message, 0, ($this->_maxsize) - 10) . ' [...]'; + } + + /* Write to socket. */ + fwrite($this->_socket, '<' . $facility_level . '>' . $message . "\n"); + + $this->_announce(array('priority' => $priority, 'message' => $message)); + } + + /** + * Converts a PEAR_LOG_* constant into a syslog LOG_* constant. + * + * This function exists because, under Windows, not all of the LOG_* + * constants have unique values. Instead, the PEAR_LOG_* were introduced + * for global use, with the conversion to the LOG_* constants kept local to + * to the syslog driver. + * + * @param int $priority PEAR_LOG_* value to convert to LOG_* value. + * + * @return The LOG_* representation of $priority. + * + * @access private + */ + function _toSyslog($priority) + { + static $priorities = array( + PEAR_LOG_EMERG => LOG_EMERG, + PEAR_LOG_ALERT => LOG_ALERT, + PEAR_LOG_CRIT => LOG_CRIT, + PEAR_LOG_ERR => LOG_ERR, + PEAR_LOG_WARNING => LOG_WARNING, + PEAR_LOG_NOTICE => LOG_NOTICE, + PEAR_LOG_INFO => LOG_INFO, + PEAR_LOG_DEBUG => LOG_DEBUG + ); + + /* If we're passed an unknown priority, default to LOG_INFO. */ + if (!is_int($priority) || !in_array($priority, $priorities)) { + return LOG_INFO; + } + + return $priorities[$priority]; + } +} diff --git a/thirdparty/pear/Log/display.php b/thirdparty/pear/Log/display.php new file mode 100644 index 0000000..9d8a849 --- /dev/null +++ b/thirdparty/pear/Log/display.php @@ -0,0 +1,108 @@ + + * @since Log 1.8.0 + * @package Log + * + * @example display.php Using the display handler. + */ +class Log_display extends Log +{ + /** + * String to output before an error message + * @var string + * @access private + */ + var $_error_prepend = ''; + + /** + * String to output after an error message + * @var string + * @access private + */ + var $_error_append = ''; + + + /** + * Constructs a new Log_display object. + * + * @param string $name Ignored. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_display($name = '', $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + + if (!empty($conf['error_prepend'])) { + $this->_error_prepend = $conf['error_prepend']; + } else { + $this->_error_prepend = ini_get('error_prepend_string'); + } + + if (!empty($conf['error_append'])) { + $this->_error_append = $conf['error_append']; + } else { + $this->_error_append = ini_get('error_append_string'); + } + } + + /** + * Writes $message to the text browser. Also, passes the message + * along to any Log_observer instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + /* Build and output the complete log line. */ + echo $this->_error_prepend . + '' . ucfirst($this->priorityToString($priority)) . ': '. + nl2br(htmlspecialchars($message)) . + $this->_error_append . "
\n"; + + /* Notify observers about this log message. */ + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } +} + +?> diff --git a/thirdparty/pear/Log/error_log.php b/thirdparty/pear/Log/error_log.php new file mode 100644 index 0000000..b432de6 --- /dev/null +++ b/thirdparty/pear/Log/error_log.php @@ -0,0 +1,104 @@ + + * @since Log 1.7.0 + * @package Log + * + * @example error_log.php Using the error_log handler. + */ +class Log_error_log extends Log +{ + /** + * The error_log() log type. + * @var integer + * @access private + */ + var $_type = PEAR_LOG_TYPE_SYSTEM; + + /** + * The type-specific destination value. + * @var string + * @access private + */ + var $_destination = ''; + + /** + * Additional headers to pass to the mail() function when the + * PEAR_LOG_TYPE_MAIL type is used. + * @var string + * @access private + */ + var $_extra_headers = ''; + + /** + * Constructs a new Log_error_log object. + * + * @param string $name Ignored. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_error_log($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_type = $name; + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + + if (!empty($conf['destination'])) { + $this->_destination = $conf['destination']; + } + if (!empty($conf['extra_headers'])) { + $this->_extra_headers = $conf['extra_headers']; + } + } + + /** + * Logs $message using PHP's error_log() function. The message is also + * passed along to any Log_observer instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + $success = error_log($this->_ident . ': ' . $message, $this->_type, + $this->_destination, $this->_extra_headers); + + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return $success; + } +} + +?> diff --git a/thirdparty/pear/Log/file.php b/thirdparty/pear/Log/file.php new file mode 100644 index 0000000..4ac38ad --- /dev/null +++ b/thirdparty/pear/Log/file.php @@ -0,0 +1,286 @@ + + * @author Roman Neuhauser + * @since Log 1.0 + * @package Log + * + * @example file.php Using the file handler. + */ +class Log_file extends Log +{ + /** + * String containing the name of the log file. + * @var string + * @access private + */ + var $_filename = 'php.log'; + + /** + * Handle to the log file. + * @var resource + * @access private + */ + var $_fp = false; + + /** + * Should new log entries be append to an existing log file, or should the + * a new log file overwrite an existing one? + * @var boolean + * @access private + */ + var $_append = true; + + /** + * Integer (in octal) containing the log file's permissions mode. + * @var integer + * @access private + */ + var $_mode = 0644; + + /** + * String containing the format of a log line. + * @var string + * @access private + */ + var $_lineFormat = '%1$s %2$s [%3$s] %4$s'; + + /** + * String containing the timestamp format. It will be passed directly to + * strftime(). Note that the timestamp string will generated using the + * current locale. + * @var string + * @access private + */ + var $_timeFormat = '%b %d %H:%M:%S'; + + /** + * Hash that maps canonical format keys to position arguments for the + * "line format" string. + * @var array + * @access private + */ + var $_formatMap = array('%{timestamp}' => '%1$s', + '%{ident}' => '%2$s', + '%{priority}' => '%3$s', + '%{message}' => '%4$s', + '%\{' => '%%{'); + + /** + * String containing the end-on-line character sequence. + * @var string + * @access private + */ + var $_eol = "\n"; + + /** + * Constructs a new Log_file object. + * + * @param string $name Ignored. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_file($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_filename = $name; + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + + if (isset($conf['append'])) { + $this->_append = $conf['append']; + } + + if (!empty($conf['mode'])) { + $this->_mode = $conf['mode']; + } + + if (!empty($conf['lineFormat'])) { + $this->_lineFormat = str_replace(array_keys($this->_formatMap), + array_values($this->_formatMap), + $conf['lineFormat']); + } + + if (!empty($conf['timeFormat'])) { + $this->_timeFormat = $conf['timeFormat']; + } + + if (!empty($conf['eol'])) { + $this->_eol = $conf['eol']; + } else { + $this->_eol = (strstr(PHP_OS, 'WIN')) ? "\r\n" : "\n"; + } + + register_shutdown_function(array(&$this, '_Log_file')); + } + + /** + * Destructor + */ + function _Log_file() + { + if ($this->_opened) { + $this->close(); + } + } + + /** + * Creates the given directory path. If the parent directories don't + * already exist, they will be created, too. + * + * @param string $path The full directory path to create. + * @param integer $mode The permissions mode with which the + * directories will be created. + * + * @return True if the full path is successfully created or already + * exists. + * + * @access private + */ + function _mkpath($path, $mode = 0700) + { + static $depth = 0; + + /* Guard against potentially infinite recursion. */ + if ($depth++ > 25) { + trigger_error("_mkpath(): Maximum recursion depth (25) exceeded", + E_USER_WARNING); + return false; + } + + /* We're only interested in the directory component of the path. */ + $path = dirname($path); + + /* If the directory already exists, return success immediately. */ + if (is_dir($path)) { + $depth = 0; + return true; + } + + /* + * In order to understand recursion, you must first understand + * recursion ... + */ + if ($this->_mkpath($path, $mode) === false) { + return false; + } + + return @mkdir($path, $mode); + } + + /** + * Opens the log file for output. If the specified log file does not + * already exist, it will be created. By default, new log entries are + * appended to the end of the log file. + * + * This is implicitly called by log(), if necessary. + * + * @access public + */ + function open() + { + if (!$this->_opened) { + /* If the log file's directory doesn't exist, create it. */ + if (!is_dir(dirname($this->_filename))) { + $this->_mkpath($this->_filename); + } + + /* Obtain a handle to the log file. */ + $this->_fp = fopen($this->_filename, ($this->_append) ? 'a' : 'w'); + + $this->_opened = ($this->_fp !== false); + + /* Attempt to set the log file's mode. */ + @chmod($this->_filename, $this->_mode); + } + + return $this->_opened; + } + + /** + * Closes the log file if it is open. + * + * @access public + */ + function close() + { + /* If the log file is open, close it. */ + if ($this->_opened && fclose($this->_fp)) { + $this->_opened = false; + } + + return ($this->_opened === false); + } + + /** + * Flushes all pending data to the file handle. + * + * @access public + * @since Log 1.8.2 + */ + function flush() + { + return fflush($this->_fp); + } + + /** + * Logs $message to the output window. The message is also passed along + * to any Log_observer instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* If the log file isn't already open, open it now. */ + if (!$this->_opened && !$this->open()) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + /* Build the string containing the complete log line. */ + $line = sprintf($this->_lineFormat, strftime($this->_timeFormat), + $this->_ident, $this->priorityToString($priority), + $message) . $this->_eol; + + /* Write the log line to the log file. */ + $success = (fwrite($this->_fp, $line) !== false); + + /* Notify observers about this log message. */ + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return $success; + } +} + +?> diff --git a/thirdparty/pear/Log/mail.php b/thirdparty/pear/Log/mail.php new file mode 100644 index 0000000..44633e5 --- /dev/null +++ b/thirdparty/pear/Log/mail.php @@ -0,0 +1,222 @@ + + * @author Jon Parise + * @since Log 1.3 + * @package Log + * + * @example mail.php Using the mail handler. + */ +class Log_mail extends Log +{ + /** + * String holding the recipient's email address. + * @var string + * @access private + */ + var $_recipient = ''; + + /** + * String holding the sender's email address. + * @var string + * @access private + */ + var $_from = ''; + + /** + * String holding the email's subject. + * @var string + * @access private + */ + var $_subject = '[Log_mail] Log message'; + + /** + * String holding an optional preamble for the log messages. + * @var string + * @access private + */ + var $_preamble = ''; + + /** + * String holding the mail message body. + * @var string + * @access private + */ + var $_message = ''; + + + /** + * Constructs a new Log_mail object. + * + * Here is how you can customize the mail driver with the conf[] hash : + * $conf['from'] : the mail's "From" header line, + * $conf['subject'] : the mail's "Subject" line. + * + * @param string $name The filename of the logfile. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_mail($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_recipient = $name; + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + + if (!empty($conf['from'])) { + $this->_from = $conf['from']; + } else { + $this->_from = ini_get('sendmail_from'); + } + + if (!empty($conf['subject'])) { + $this->_subject = $conf['subject']; + } + + if (!empty($conf['preamble'])) { + $this->_preamble = $conf['preamble']; + } + + /* register the destructor */ + register_shutdown_function(array(&$this, '_Log_mail')); + } + + /** + * Destructor. Calls close(). + * + * @access private + */ + function _Log_mail() + { + $this->close(); + } + + /** + * Starts a new mail message. + * This is implicitly called by log(), if necessary. + * + * @access public + */ + function open() + { + if (!$this->_opened) { + if (!empty($this->_preamble)) { + $this->_message = $this->_preamble . "\n\n"; + } + $this->_opened = true; + } + + return $this->_opened; + } + + /** + * Closes the message, if it is open, and sends the mail. + * This is implicitly called by the destructor, if necessary. + * + * @access public + */ + function close() + { + if ($this->_opened) { + if (!empty($this->_message)) { + $headers = "From: $this->_from\n"; + $headers .= "User-Agent: Log_mail"; + + if (mail($this->_recipient, $this->_subject, $this->_message, + $headers) == false) { + error_log("Log_mail: Failure executing mail()", 0); + return false; + } + + /* Clear the message string now that the email has been sent. */ + $this->_message = ''; + } + $this->_opened = false; + } + + return ($this->_opened === false); + } + + /** + * Flushes the log output by forcing the email message to be sent now. + * Events that are logged after flush() is called will be appended to a + * new email message. + * + * @access public + * @since Log 1.8.2 + */ + function flush() + { + /* + * It's sufficient to simply call close() to flush the output. + * The next call to log() will cause the handler to be reopened. + */ + return $this->close(); + } + + /** + * Writes $message to the currently open mail message. + * Calls open(), if necessary. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* If the message isn't open and can't be opened, return failure. */ + if (!$this->_opened && !$this->open()) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + $entry = sprintf("%s %s [%s] %s\n", strftime('%b %d %H:%M:%S'), + $this->_ident, Log::priorityToString($priority), + $message); + + $this->_message .= $entry; + + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } +} + +?> diff --git a/thirdparty/pear/Log/mcal.php b/thirdparty/pear/Log/mcal.php new file mode 100644 index 0000000..37ef3aa --- /dev/null +++ b/thirdparty/pear/Log/mcal.php @@ -0,0 +1,171 @@ + + * @since Horde 1.3 + * @since Log 1.0 + * @package Log + */ +class Log_mcal extends Log { + + /** + * holding the calendar specification to connect to. + * @var string + * @access private + */ + var $_calendar = '{localhost/mstore}'; + + /** + * holding the username to use. + * @var string + * @access private + */ + var $_username = ''; + + /** + * holding the password to use. + * @var string + * @access private + */ + var $_password = ''; + + /** + * holding the options to pass to the calendar stream. + * @var integer + * @access private + */ + var $_options = 0; + + /** + * ResourceID of the MCAL stream. + * @var string + * @access private + */ + var $_stream = ''; + + /** + * Integer holding the log facility to use. + * @var string + * @access private + */ + var $_name = LOG_SYSLOG; + + + /** + * Constructs a new Log_mcal object. + * + * @param string $name The category to use for our events. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_mcal($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_name = $name; + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + $this->_calendar = $conf['calendar']; + $this->_username = $conf['username']; + $this->_password = $conf['password']; + $this->_options = $conf['options']; + } + + /** + * Opens a calendar stream, if it has not already been + * opened. This is implicitly called by log(), if necessary. + * @access public + */ + function open() + { + if (!$this->_opened) { + $this->_stream = mcal_open($this->_calendar, $this->_username, + $this->_password, $this->_options); + $this->_opened = true; + } + + return $this->_opened; + } + + /** + * Closes the calendar stream, if it is open. + * @access public + */ + function close() + { + if ($this->_opened) { + mcal_close($this->_stream); + $this->_opened = false; + } + + return ($this->_opened === false); + } + + /** + * Logs $message and associated information to the currently open + * calendar stream. Calls open() if necessary. Also passes the + * message along to any Log_observer instances that are observing + * this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* If the connection isn't open and can't be opened, return failure. */ + if (!$this->_opened && !$this->open()) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + $date_str = date('Y:n:j:G:i:s'); + $dates = explode(':', $date_str); + + mcal_event_init($this->_stream); + mcal_event_set_title($this->_stream, $this->_ident); + mcal_event_set_category($this->_stream, $this->_name); + mcal_event_set_description($this->_stream, $message); + mcal_event_add_attribute($this->_stream, 'priority', $priority); + mcal_event_set_start($this->_stream, $dates[0], $dates[1], $dates[2], + $dates[3], $dates[4], $dates[5]); + mcal_event_set_end($this->_stream, $dates[0], $dates[1], $dates[2], + $dates[3], $dates[4], $dates[5]); + mcal_append_event($this->_stream); + + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } +} + +?> diff --git a/thirdparty/pear/Log/null.php b/thirdparty/pear/Log/null.php new file mode 100644 index 0000000..decbca5 --- /dev/null +++ b/thirdparty/pear/Log/null.php @@ -0,0 +1,68 @@ + + * @since Log 1.8.2 + * @package Log + * + * @example null.php Using the null handler. + */ +class Log_null extends Log +{ + /** + * Constructs a new Log_null object. + * + * @param string $name Ignored. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_null($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + } + + /** + * Simply consumes the log event. The message will still be passed + * along to any Log_observer instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } +} + +?> diff --git a/thirdparty/pear/Log/observer.php b/thirdparty/pear/Log/observer.php new file mode 100644 index 0000000..08aafe1 --- /dev/null +++ b/thirdparty/pear/Log/observer.php @@ -0,0 +1,126 @@ + + * @since Horde 1.3 + * @since Log 1.0 + * @package Log + * + * @example observer_mail.php An example Log_observer implementation. + */ +class Log_observer +{ + /** + * Instance-specific unique identification number. + * + * @var integer + * @access private + */ + var $_id = 0; + + /** + * The minimum priority level of message that we want to hear about. + * PEAR_LOG_EMERG is the highest priority, so we will only hear messages + * with an integer priority value less than or equal to ours. It defaults + * to PEAR_LOG_INFO, which listens to everything except PEAR_LOG_DEBUG. + * + * @var string + * @access private + */ + var $_priority = PEAR_LOG_INFO; + + /** + * Creates a new basic Log_observer instance. + * + * @param integer $priority The highest priority at which to receive + * log event notifications. + * + * @access public + */ + function Log_observer($priority = PEAR_LOG_INFO) + { + $this->_id = md5(microtime()); + $this->_priority = $priority; + } + + /** + * Attempts to return a new concrete Log_observer instance of the requested + * type. + * + * @param string $type The type of concreate Log_observer subclass + * to return. + * @param integer $priority The highest priority at which to receive + * log event notifications. + * @param array $conf Optional associative array of additional + * configuration values. + * + * @return object The newly created concrete Log_observer + * instance, or an false on an error. + */ + function &factory($type, $priority = PEAR_LOG_INFO, $conf = array()) + { + $type = strtolower($type); + $class = 'Log_observer_' . $type; + + /* Support both the new-style and old-style file naming conventions. */ + if (file_exists(dirname(__FILE__) . '/observer_' . $type . '.php')) { + $classfile = 'Log/observer_' . $type . '.php'; + $newstyle = true; + } else { + $classfile = 'Log/' . $type . '.php'; + $newstyle = false; + } + + /* Issue a warning if the old-style conventions are being used. */ + if (!$newstyle) + { + trigger_error('Using old-style Log_observer conventions', + E_USER_WARNING); + } + + /* + * Attempt to include our version of the named class, but don't treat + * a failure as fatal. The caller may have already included their own + * version of the named class. + */ + @include_once $classfile; + + /* If the class exists, return a new instance of it. */ + if (class_exists($class)) { + /* Support both new-style and old-style construction. */ + if ($newstyle) { + return new $class($priority, $conf); + } else { + return new $class($priority); + } + } + + return false; + } + + /** + * This is a stub method to make sure that Log_Observer classes do + * something when they are notified of a message. The default behavior + * is to just print the message, which is obviously not desireable in + * practically any situation - which is why you need to override this + * method. :) + * + * @param array $event A hash describing the log event. + */ + function notify($event) + { + print_r($event); + } +} + +?> diff --git a/thirdparty/pear/Log/sql.php b/thirdparty/pear/Log/sql.php new file mode 100644 index 0000000..4fb5f07 --- /dev/null +++ b/thirdparty/pear/Log/sql.php @@ -0,0 +1,225 @@ + + * @since Horde 1.3 + * @since Log 1.0 + * @package Log + * + * @example sql.php Using the SQL handler. + */ +class Log_sql extends Log { + + /** + * Array containing the dsn information. + * @var string + * @access private + */ + var $_dsn = ''; + + /** + * Object holding the database handle. + * @var object + * @access private + */ + var $_db = null; + + /** + * Flag indicating that we're using an existing database connection. + * @var boolean + * @access private + */ + var $_existingConnection = false; + + /** + * String holding the database table to use. + * @var string + * @access private + */ + var $_table = 'log_table'; + + /** + * String holding the name of the ID sequence. + * @var string + * @access private + */ + var $_sequence = 'log_id'; + + /** + * Maximum length of the $ident string. This corresponds to the size of + * the 'ident' column in the SQL table. + * @var integer + * @access private + */ + var $_identLimit = 16; + + + /** + * Constructs a new sql logging object. + * + * @param string $name The target SQL table. + * @param string $ident The identification field. + * @param array $conf The connection configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_sql($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_table = $name; + $this->_mask = Log::UPTO($level); + + /* If a specific sequence name was provided, use it. */ + if (!empty($conf['sequence'])) { + $this->_sequence = $conf['sequence']; + } + + /* If a specific sequence name was provided, use it. */ + if (isset($conf['identLimit'])) { + $this->_identLimit = $conf['identLimit']; + } + + /* Now that the ident limit is confirmed, set the ident string. */ + $this->setIdent($ident); + + /* If an existing database connection was provided, use it. */ + if (isset($conf['db'])) { + $this->_db = &$conf['db']; + $this->_existingConnection = true; + $this->_opened = true; + } else { + $this->_dsn = $conf['dsn']; + } + } + + /** + * Opens a connection to the database, if it has not already + * been opened. This is implicitly called by log(), if necessary. + * + * @return boolean True on success, false on failure. + * @access public + */ + function open() + { + if (!$this->_opened) { + $this->_db = &DB::connect($this->_dsn, true); + if (DB::isError($this->_db)) { + return false; + } + $this->_opened = true; + } + + return $this->_opened; + } + + /** + * Closes the connection to the database if it is still open and we were + * the ones that opened it. It is the caller's responsible to close an + * existing connection that was passed to us via $conf['db']. + * + * @return boolean True on success, false on failure. + * @access public + */ + function close() + { + if ($this->_opened && !$this->_existingConnection) { + $this->_opened = false; + return $this->_db->disconnect(); + } + + return ($this->_opened === false); + } + + /** + * Sets this Log instance's identification string. Note that this + * SQL-specific implementation will limit the length of the $ident string + * to sixteen (16) characters. + * + * @param string $ident The new identification string. + * + * @access public + * @since Log 1.8.5 + */ + function setIdent($ident) + { + $this->_ident = substr($ident, 0, $this->_identLimit); + } + + /** + * Inserts $message to the currently open database. Calls open(), + * if necessary. Also passes the message along to any Log_observer + * instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* If the connection isn't open and can't be opened, return failure. */ + if (!$this->_opened && !$this->open()) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + /* Build the SQL query for this log entry insertion. */ + $id = $this->_db->nextId($this->_sequence); + $q = sprintf('insert into %s (id, logtime, ident, priority, message)' . + 'values(%d, CURRENT_TIMESTAMP, %s, %d, %s)', + $this->_table, $id, $this->_db->quote($this->_ident), + $priority, $this->_db->quote($message)); + + $result = $this->_db->query($q); + if (DB::isError($result)) { + return false; + } + + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } +} + +?> diff --git a/thirdparty/pear/Log/sqlite.php b/thirdparty/pear/Log/sqlite.php new file mode 100644 index 0000000..9b7392f --- /dev/null +++ b/thirdparty/pear/Log/sqlite.php @@ -0,0 +1,238 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ + +/** + * The Log_sqlite class is a concrete implementation of the Log:: + * abstract class which sends messages to an Sqlite database. + * Each entry occupies a separate row in the database. + * + * This implementation uses PHP native Sqlite functions. + * + * CREATE TABLE log_table ( + * id INTEGER PRIMARY KEY NOT NULL, + * logtime NOT NULL, + * ident CHAR(16) NOT NULL, + * priority INT NOT NULL, + * message + * ); + * + * @author Bertrand Mansion + * @author Jon Parise + * @since Log 1.8.3 + * @package Log + * + * @example sqlite.php Using the Sqlite handler. + */ +class Log_sqlite extends Log +{ + /** + * Array containing the connection defaults + * @var array + * @access private + */ + var $_options = array('mode' => 0666, + 'persistent' => false); + + /** + * Object holding the database handle. + * @var object + * @access private + */ + var $_db = null; + + /** + * Flag indicating that we're using an existing database connection. + * @var boolean + * @access private + */ + var $_existingConnection = false; + + /** + * String holding the database table to use. + * @var string + * @access private + */ + var $_table = 'log_table'; + + + /** + * Constructs a new sql logging object. + * + * @param string $name The target SQL table. + * @param string $ident The identification field. + * @param mixed $conf Can be an array of configuration options used + * to open a new database connection + * or an already opened sqlite connection. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_sqlite($name, $ident = '', &$conf, $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_table = $name; + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + + if (is_array($conf)) { + foreach ($conf as $k => $opt) { + $this->_options[$k] = $opt; + } + } else { + // If an existing database connection was provided, use it. + $this->_db =& $conf; + $this->_existingConnection = true; + } + } + + /** + * Opens a connection to the database, if it has not already + * been opened. This is implicitly called by log(), if necessary. + * + * @return boolean True on success, false on failure. + * @access public + */ + function open() + { + if (is_resource($this->_db)) { + $this->_opened = true; + return $this->_createTable(); + } else { + /* Set the connection function based on the 'persistent' option. */ + if (empty($this->_options['persistent'])) { + $connectFunction = 'sqlite_open'; + } else { + $connectFunction = 'sqlite_popen'; + } + + /* Attempt to connect to the database. */ + if ($this->_db = $connectFunction($this->_options['filename'], + (int)$this->_options['mode'], + $error)) { + $this->_opened = true; + return $this->_createTable(); + } + } + + return $this->_opened; + } + + /** + * Closes the connection to the database if it is still open and we were + * the ones that opened it. It is the caller's responsible to close an + * existing connection that was passed to us via $conf['db']. + * + * @return boolean True on success, false on failure. + * @access public + */ + function close() + { + /* We never close existing connections. */ + if ($this->_existingConnection) { + return false; + } + + if ($this->_opened) { + $this->_opened = false; + sqlite_close($this->_db); + } + + return ($this->_opened === false); + } + + /** + * Inserts $message to the currently open database. Calls open(), + * if necessary. Also passes the message along to any Log_observer + * instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* If the connection isn't open and can't be opened, return failure. */ + if (!$this->_opened && !$this->open()) { + return false; + } + + // Extract the string representation of the message. + $message = $this->_extractMessage($message); + + // Build the SQL query for this log entry insertion. + $q = sprintf('INSERT INTO [%s] (logtime, ident, priority, message) ' . + "VALUES ('%s', '%s', %d, '%s')", + $this->_table, + strftime('%Y-%m-%d %H:%M:%S', time()), + sqlite_escape_string($this->_ident), + $priority, + sqlite_escape_string($message)); + if (!($res = @sqlite_unbuffered_query($this->_db, $q))) { + return false; + } + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } + + /** + * Checks whether the log table exists and creates it if necessary. + * + * @return boolean True on success or false on failure. + * @access private + */ + function _createTable() + { + $q = "SELECT name FROM sqlite_master WHERE name='" . $this->_table . + "' AND type='table'"; + + $res = sqlite_query($this->_db, $q); + + if (sqlite_num_rows($res) == 0) { + $q = 'CREATE TABLE [' . $this->_table . '] (' . + 'id INTEGER PRIMARY KEY NOT NULL, ' . + 'logtime NOT NULL, ' . + 'ident CHAR(16) NOT NULL, ' . + 'priority INT NOT NULL, ' . + 'message)'; + + if (!($res = sqlite_unbuffered_query($this->_db, $q))) { + return false; + } + } + + return true; + } +} + +?> diff --git a/thirdparty/pear/Log/syslog.php b/thirdparty/pear/Log/syslog.php new file mode 100644 index 0000000..ba09472 --- /dev/null +++ b/thirdparty/pear/Log/syslog.php @@ -0,0 +1,160 @@ + + * @since Horde 1.3 + * @since Log 1.0 + * @package Log + * + * @example syslog.php Using the syslog handler. + */ +class Log_syslog extends Log +{ + /** + * Integer holding the log facility to use. + * @var string + * @access private + */ + var $_name = LOG_SYSLOG; + + /** + * Constructs a new syslog object. + * + * @param string $name The syslog facility. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_syslog($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + /* Ensure we have a valid integer value for $name. */ + if (empty($name) || !is_int($name)) { + $name = LOG_SYSLOG; + } + + $this->_id = md5(microtime()); + $this->_name = $name; + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + } + + /** + * Opens a connection to the system logger, if it has not already + * been opened. This is implicitly called by log(), if necessary. + * @access public + */ + function open() + { + if (!$this->_opened) { + openlog($this->_ident, LOG_PID, $this->_name); + $this->_opened = true; + } + + return $this->_opened; + } + + /** + * Closes the connection to the system logger, if it is open. + * @access public + */ + function close() + { + if ($this->_opened) { + closelog(); + $this->_opened = false; + } + + return ($this->_opened === false); + } + + /** + * Sends $message to the currently open syslog connection. Calls + * open() if necessary. Also passes the message along to any Log_observer + * instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param int $priority (optional) The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* If the connection isn't open and can't be opened, return failure. */ + if (!$this->_opened && !$this->open()) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + if (!syslog($this->_toSyslog($priority), $message)) { + return false; + } + + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } + + /** + * Converts a PEAR_LOG_* constant into a syslog LOG_* constant. + * + * This function exists because, under Windows, not all of the LOG_* + * constants have unique values. Instead, the PEAR_LOG_* were introduced + * for global use, with the conversion to the LOG_* constants kept local to + * to the syslog driver. + * + * @param int $priority PEAR_LOG_* value to convert to LOG_* value. + * + * @return The LOG_* representation of $priority. + * + * @access private + */ + function _toSyslog($priority) + { + static $priorities = array( + PEAR_LOG_EMERG => LOG_EMERG, + PEAR_LOG_ALERT => LOG_ALERT, + PEAR_LOG_CRIT => LOG_CRIT, + PEAR_LOG_ERR => LOG_ERR, + PEAR_LOG_WARNING => LOG_WARNING, + PEAR_LOG_NOTICE => LOG_NOTICE, + PEAR_LOG_INFO => LOG_INFO, + PEAR_LOG_DEBUG => LOG_DEBUG + ); + + /* If we're passed an unknown priority, default to LOG_INFO. */ + if (!is_int($priority) || !in_array($priority, $priorities)) { + return LOG_INFO; + } + + return $priorities[$priority]; + } +} +?> diff --git a/thirdparty/pear/Log/win.php b/thirdparty/pear/Log/win.php new file mode 100644 index 0000000..9c59e97 --- /dev/null +++ b/thirdparty/pear/Log/win.php @@ -0,0 +1,256 @@ + + * @since Log 1.7.0 + * @package Log + * + * @example win.php Using the window handler. + */ +class Log_win extends Log +{ + /** + * The name of the output window. + * @var string + * @access private + */ + var $_name = 'LogWindow'; + + /** + * The title of the output window. + * @var string + * @access private + */ + var $_title = 'Log Output Window'; + + /** + * Mapping of log priorities to colors. + * @var array + * @access private + */ + var $_colors = array( + PEAR_LOG_EMERG => 'red', + PEAR_LOG_ALERT => 'orange', + PEAR_LOG_CRIT => 'yellow', + PEAR_LOG_ERR => 'green', + PEAR_LOG_WARNING => 'blue', + PEAR_LOG_NOTICE => 'indigo', + PEAR_LOG_INFO => 'violet', + PEAR_LOG_DEBUG => 'black' + ); + + /** + * String buffer that holds line that are pending output. + * @var array + * @access private + */ + var $_buffer = array(); + + /** + * Constructs a new Log_win object. + * + * @param string $name Ignored. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_win($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_name = $name; + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + + if (isset($conf['title'])) { + $this->_title = $conf['title']; + } + if (isset($conf['colors']) && is_array($conf['colors'])) { + $this->_colors = $conf['colors']; + } + + register_shutdown_function(array(&$this, '_Log_win')); + } + + /** + * Destructor + */ + function _Log_win() + { + if ($this->_opened || (count($this->_buffer) > 0)) { + $this->close(); + } + } + + /** + * The first time open() is called, it will open a new browser window and + * prepare it for output. + * + * This is implicitly called by log(), if necessary. + * + * @access public + */ + function open() + { + if (!$this->_opened) { + $win = $this->_name; + + if (!empty($this->_ident)) { + $identHeader = "$win.document.writeln('Ident')"; + } else { + $identHeader = ''; + } + + echo <<< END_OF_SCRIPT + +END_OF_SCRIPT; + $this->_opened = true; + } + + return $this->_opened; + } + + /** + * Closes the output stream if it is open. If there are still pending + * lines in the output buffer, the output window will be opened so that + * the buffer can be drained. + * + * @access public + */ + function close() + { + /* + * If there are still lines waiting to be written, open the output + * window so that we can drain the buffer. + */ + if (!$this->_opened && (count($this->_buffer) > 0)) { + $this->open(); + } + + if ($this->_opened) { + $this->_writeln(''); + $this->_writeln(''); + $this->_opened = false; + } + + return ($this->_opened === false); + } + + /** + * Writes a single line of text to the output window. + * + * @param string $line The line of text to write. + * + * @access private + */ + function _writeln($line) + { + /* Add this line to our output buffer. */ + $this->_buffer[] = $line; + + /* Buffer the output until this page's headers have been sent. */ + if (!headers_sent()) { + return; + } + + /* If we haven't already opened the output window, do so now. */ + if (!$this->_opened && !$this->open()) { + return false; + } + + /* Drain the buffer to the output window. */ + $win = $this->_name; + foreach ($this->_buffer as $line) { + echo "\n"; + } + + /* Now that the buffer has been drained, clear it. */ + $this->_buffer = array(); + } + + /** + * Logs $message to the output window. The message is also passed along + * to any Log_observer instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + list($usec, $sec) = explode(' ', microtime()); + + /* Build the output line that contains the log entry row. */ + $line = ''; + $line .= sprintf('%s.%s', + strftime('%T', $sec), substr($usec, 2, 2)); + if (!empty($this->_ident)) { + $line .= '' . $this->_ident . ''; + } + $line .= '' . ucfirst($this->priorityToString($priority)) . ''; + $line .= sprintf('%s', + $this->_colors[$priority], + preg_replace('/\r\n|\n|\r/', '
', $message)); + $line .= ''; + + $this->_writeln($line); + + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } +} + +?> diff --git a/thirdparty/pear/PEAR.php b/thirdparty/pear/PEAR.php new file mode 100644 index 0000000..674b8e1 --- /dev/null +++ b/thirdparty/pear/PEAR.php @@ -0,0 +1,1056 @@ + | +// | Stig Bakken | +// | Tomas V.V.Cox | +// +--------------------------------------------------------------------+ +// +// $Id$ +// + +define('PEAR_ERROR_RETURN', 1); +define('PEAR_ERROR_PRINT', 2); +define('PEAR_ERROR_TRIGGER', 4); +define('PEAR_ERROR_DIE', 8); +define('PEAR_ERROR_CALLBACK', 16); +/** + * WARNING: obsolete + * @deprecated + */ +define('PEAR_ERROR_EXCEPTION', 32); +define('PEAR_ZE2', (function_exists('version_compare') && + version_compare(zend_version(), "2-dev", "ge"))); + +if (substr(PHP_OS, 0, 3) == 'WIN') { + define('OS_WINDOWS', true); + define('OS_UNIX', false); + define('PEAR_OS', 'Windows'); +} else { + define('OS_WINDOWS', false); + define('OS_UNIX', true); + define('PEAR_OS', 'Unix'); // blatant assumption +} + +// instant backwards compatibility +if (!defined('PATH_SEPARATOR')) { + if (OS_WINDOWS) { + define('PATH_SEPARATOR', ';'); + } else { + define('PATH_SEPARATOR', ':'); + } +} + +$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; +$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; +$GLOBALS['_PEAR_destructor_object_list'] = array(); +$GLOBALS['_PEAR_shutdown_funcs'] = array(); +$GLOBALS['_PEAR_error_handler_stack'] = array(); + +ini_set('track_errors', true); + +/** + * Base class for other PEAR classes. Provides rudimentary + * emulation of destructors. + * + * If you want a destructor in your class, inherit PEAR and make a + * destructor method called _yourclassname (same name as the + * constructor, but with a "_" prefix). Also, in your constructor you + * have to call the PEAR constructor: $this->PEAR();. + * The destructor method will be called without parameters. Note that + * at in some SAPI implementations (such as Apache), any output during + * the request shutdown (in which destructors are called) seems to be + * discarded. If you need to get any debug information from your + * destructor, use error_log(), syslog() or something similar. + * + * IMPORTANT! To use the emulated destructors you need to create the + * objects by reference: $obj =& new PEAR_child; + * + * @since PHP 4.0.2 + * @author Stig Bakken + * @see http://pear.php.net/manual/ + */ +class PEAR +{ + // {{{ properties + + /** + * Whether to enable internal debug messages. + * + * @var bool + * @access private + */ + var $_debug = false; + + /** + * Default error mode for this object. + * + * @var int + * @access private + */ + var $_default_error_mode = null; + + /** + * Default error options used for this object when error mode + * is PEAR_ERROR_TRIGGER. + * + * @var int + * @access private + */ + var $_default_error_options = null; + + /** + * Default error handler (callback) for this object, if error mode is + * PEAR_ERROR_CALLBACK. + * + * @var string + * @access private + */ + var $_default_error_handler = ''; + + /** + * Which class to use for error objects. + * + * @var string + * @access private + */ + var $_error_class = 'PEAR_Error'; + + /** + * An array of expected errors. + * + * @var array + * @access private + */ + var $_expected_errors = array(); + + // }}} + + // {{{ constructor + + /** + * Constructor. Registers this object in + * $_PEAR_destructor_object_list for destructor emulation if a + * destructor object exists. + * + * @param string $error_class (optional) which class to use for + * error objects, defaults to PEAR_Error. + * @access public + * @return void + */ + function PEAR($error_class = null) + { + $classname = strtolower(get_class($this)); + if ($this->_debug) { + print "PEAR constructor called, class=$classname\n"; + } + if ($error_class !== null) { + $this->_error_class = $error_class; + } + while ($classname && strcasecmp($classname, "pear")) { + $destructor = "_$classname"; + if (method_exists($this, $destructor)) { + global $_PEAR_destructor_object_list; + $_PEAR_destructor_object_list[] = &$this; + static $registered = false; + if (!$registered) { + register_shutdown_function("_PEAR_call_destructors"); + $registered = true; + } + break; + } else { + $classname = get_parent_class($classname); + } + } + } + + // }}} + // {{{ destructor + + /** + * Destructor (the emulated type of...). Does nothing right now, + * but is included for forward compatibility, so subclass + * destructors should always call it. + * + * See the note in the class desciption about output from + * destructors. + * + * @access public + * @return void + */ + function _PEAR() { + if ($this->_debug) { + printf("PEAR destructor called, class=%s\n", strtolower(get_class($this))); + } + } + + // }}} + // {{{ getStaticProperty() + + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); + * You MUST use a reference, or they will not persist! + * + * @access public + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + */ + function &getStaticProperty($class, $var) + { + static $properties; + return $properties[$class][$var]; + } + + // }}} + // {{{ registerShutdownFunc() + + /** + * Use this function to register a shutdown method for static + * classes. + * + * @access public + * @param mixed $func The function name (or array of class/method) to call + * @param mixed $args The arguments to pass to the function + * @return void + */ + function registerShutdownFunc($func, $args = array()) + { + $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); + } + + // }}} + // {{{ isError() + + /** + * Tell whether a value is a PEAR error. + * + * @param mixed $data the value to test + * @param int $code if $data is an error object, return true + * only if $code is a string and + * $obj->getMessage() == $code or + * $code is an integer and $obj->getCode() == $code + * @access public + * @return bool true if parameter is an error + */ + function isError($data, $code = null) + { + if (is_a($data, 'PEAR_Error')) { + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() == $code; + } else { + return $data->getCode() == $code; + } + } + return false; + } + + // }}} + // {{{ setErrorHandling() + + /** + * Sets how errors generated by this object should be handled. + * Can be invoked both in objects and statically. If called + * statically, setErrorHandling sets the default behaviour for all + * PEAR objects. If called in an object, setErrorHandling sets + * the default behaviour for that object. + * + * @param int $mode + * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. + * + * @param mixed $options + * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one + * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * + * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected + * to be the callback function or method. A callback + * function is a string with the name of the function, a + * callback method is an array of two elements: the element + * at index 0 is the object, and the element at index 1 is + * the name of the method to call in the object. + * + * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is + * a printf format string used when printing the error + * message. + * + * @access public + * @return void + * @see PEAR_ERROR_RETURN + * @see PEAR_ERROR_PRINT + * @see PEAR_ERROR_TRIGGER + * @see PEAR_ERROR_DIE + * @see PEAR_ERROR_CALLBACK + * @see PEAR_ERROR_EXCEPTION + * + * @since PHP 4.0.5 + */ + + function setErrorHandling($mode = null, $options = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + $setmode = &$this->_default_error_mode; + $setoptions = &$this->_default_error_options; + } else { + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + } + + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + } + + // }}} + // {{{ expectError() + + /** + * This method is used to tell which errors you expect to get. + * Expected errors are always returned with error mode + * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, + * and this method pushes a new element onto it. The list of + * expected errors are in effect until they are popped off the + * stack with the popExpect() method. + * + * Note that this method can not be called statically + * + * @param mixed $code a single error code or an array of error codes to expect + * + * @return int the new depth of the "expected errors" stack + * @access public + */ + function expectError($code = '*') + { + if (is_array($code)) { + array_push($this->_expected_errors, $code); + } else { + array_push($this->_expected_errors, array($code)); + } + return sizeof($this->_expected_errors); + } + + // }}} + // {{{ popExpect() + + /** + * This method pops one element off the expected error codes + * stack. + * + * @return array the list of error codes that were popped + */ + function popExpect() + { + return array_pop($this->_expected_errors); + } + + // }}} + // {{{ _checkDelExpect() + + /** + * This method checks unsets an error code if available + * + * @param mixed error code + * @return bool true if the error code was unset, false otherwise + * @access private + * @since PHP 4.3.0 + */ + function _checkDelExpect($error_code) + { + $deleted = false; + + foreach ($this->_expected_errors AS $key => $error_array) { + if (in_array($error_code, $error_array)) { + unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); + $deleted = true; + } + + // clean up empty arrays + if (0 == count($this->_expected_errors[$key])) { + unset($this->_expected_errors[$key]); + } + } + return $deleted; + } + + // }}} + // {{{ delExpect() + + /** + * This method deletes all occurences of the specified element from + * the expected error codes stack. + * + * @param mixed $error_code error code that should be deleted + * @return mixed list of error codes that were deleted or error + * @access public + * @since PHP 4.3.0 + */ + function delExpect($error_code) + { + $deleted = false; + + if ((is_array($error_code) && (0 != count($error_code)))) { + // $error_code is a non-empty array here; + // we walk through it trying to unset all + // values + foreach($error_code as $key => $error) { + if ($this->_checkDelExpect($error)) { + $deleted = true; + } else { + $deleted = false; + } + } + return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } elseif (!empty($error_code)) { + // $error_code comes alone, trying to unset it + if ($this->_checkDelExpect($error_code)) { + return true; + } else { + return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } + } else { + // $error_code is empty + return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + } + } + + // }}} + // {{{ raiseError() + + /** + * This method is a wrapper that returns an instance of the + * configured error class with this object's default error + * handling applied. If the $mode and $options parameters are not + * specified, the object's defaults are used. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION. + * + * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter + * specifies the PHP-internal error level (one of + * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * If $mode is PEAR_ERROR_CALLBACK, this + * parameter specifies the callback function or + * method. In other error modes this parameter + * is ignored. + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @param string $error_class The returned error object will be + * instantiated from this class, if specified. + * + * @param bool $skipmsg If true, raiseError will only pass error codes, + * the error message parameter will be dropped. + * + * @access public + * @return object a PEAR error object + * @see PEAR::setErrorHandling + * @since PHP 4.0.5 + */ + function raiseError($message = null, + $code = null, + $mode = null, + $options = null, + $userinfo = null, + $error_class = null, + $skipmsg = false) + { + // The error is yet a PEAR error object + if (is_object($message)) { + $code = $message->getCode(); + $userinfo = $message->getUserInfo(); + $error_class = $message->getType(); + $message->error_message_prefix = ''; + $message = $message->getMessage(); + } + + if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) { + if ($exp[0] == "*" || + (is_int(reset($exp)) && in_array($code, $exp)) || + (is_string(reset($exp)) && in_array($message, $exp))) { + $mode = PEAR_ERROR_RETURN; + } + } + // No mode given, try global ones + if ($mode === null) { + // Class error handler + if (isset($this) && isset($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + // Global error handler + } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { + $mode = $GLOBALS['_PEAR_default_error_mode']; + $options = $GLOBALS['_PEAR_default_error_options']; + } + } + + if ($error_class !== null) { + $ec = $error_class; + } elseif (isset($this) && isset($this->_error_class)) { + $ec = $this->_error_class; + } else { + $ec = 'PEAR_Error'; + } + if ($skipmsg) { + return new $ec($code, $mode, $options, $userinfo); + } else { + return new $ec($message, $code, $mode, $options, $userinfo); + } + } + + // }}} + // {{{ throwError() + + /** + * Simpler form of raiseError with fewer options. In most cases + * message, code and userinfo are enough. + * + * @param string $message + * + */ + function throwError($message = null, + $code = null, + $userinfo = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + return $this->raiseError($message, $code, null, null, $userinfo); + } else { + return PEAR::raiseError($message, $code, null, null, $userinfo); + } + } + + // }}} + function staticPushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + $stack[] = array($def_mode, $def_options); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $def_mode = $mode; + $def_options = $options; + break; + + case PEAR_ERROR_CALLBACK: + $def_mode = $mode; + // class/object method callback + if (is_callable($options)) { + $def_options = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + $stack[] = array($mode, $options); + return true; + } + + function staticPopErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + return true; + } + + // {{{ pushErrorHandling() + + /** + * Push a new error handler on top of the error handler options stack. With this + * you can easily override the actual error handler for some code and restore + * it later with popErrorHandling. + * + * @param mixed $mode (same as setErrorHandling) + * @param mixed $options (same as setErrorHandling) + * + * @return bool Always true + * + * @see PEAR::setErrorHandling + */ + function pushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + if (isset($this) && is_a($this, 'PEAR')) { + $def_mode = &$this->_default_error_mode; + $def_options = &$this->_default_error_options; + } else { + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + } + $stack[] = array($def_mode, $def_options); + + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + $stack[] = array($mode, $options); + return true; + } + + // }}} + // {{{ popErrorHandling() + + /** + * Pop the last error handler used + * + * @return bool Always true + * + * @see PEAR::pushErrorHandling + */ + function popErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + return true; + } + + // }}} + // {{{ loadExtension() + + /** + * OS independant PHP extension load. Remember to take care + * on the correct extension name for case sensitive OSes. + * + * @param string $ext The extension name + * @return bool Success or not on the dl() call + */ + function loadExtension($ext) + { + if (!extension_loaded($ext)) { + // if either returns true dl() will produce a FATAL error, stop that + if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { + return false; + } + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + } + return true; + } + + // }}} +} + +// {{{ _PEAR_call_destructors() + +function _PEAR_call_destructors() +{ + global $_PEAR_destructor_object_list; + if (is_array($_PEAR_destructor_object_list) && + sizeof($_PEAR_destructor_object_list)) + { + reset($_PEAR_destructor_object_list); + if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) { + $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); + } + while (list($k, $objref) = each($_PEAR_destructor_object_list)) { + $classname = get_class($objref); + while ($classname) { + $destructor = "_$classname"; + if (method_exists($objref, $destructor)) { + $objref->$destructor(); + break; + } else { + $classname = get_parent_class($classname); + } + } + } + // Empty the object list to ensure that destructors are + // not called more than once. + $_PEAR_destructor_object_list = array(); + } + + // Now call the shutdown functions + if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) { + foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { + call_user_func_array($value[0], $value[1]); + } + } +} + +// }}} + +class PEAR_Error +{ + // {{{ properties + + var $error_message_prefix = ''; + var $mode = PEAR_ERROR_RETURN; + var $level = E_USER_NOTICE; + var $code = -1; + var $message = ''; + var $userinfo = ''; + var $backtrace = null; + + // }}} + // {{{ constructor + + /** + * PEAR_Error constructor + * + * @param string $message message + * + * @param int $code (optional) error code + * + * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, + * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION + * + * @param mixed $options (optional) error level, _OR_ in the case of + * PEAR_ERROR_CALLBACK, the callback function or object/method + * tuple. + * + * @param string $userinfo (optional) additional user/debug info + * + * @access public + * + */ + function PEAR_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + if ($mode === null) { + $mode = PEAR_ERROR_RETURN; + } + $this->message = $message; + $this->code = $code; + $this->mode = $mode; + $this->userinfo = $userinfo; + if (function_exists("debug_backtrace")) { + if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) { + $this->backtrace = debug_backtrace(); + } + } + if ($mode & PEAR_ERROR_CALLBACK) { + $this->level = E_USER_NOTICE; + $this->callback = $options; + } else { + if ($options === null) { + $options = E_USER_NOTICE; + } + $this->level = $options; + $this->callback = null; + } + if ($this->mode & PEAR_ERROR_PRINT) { + if (is_null($options) || is_int($options)) { + $format = "%s"; + } else { + $format = $options; + } + printf($format, $this->getMessage()); + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + trigger_error($this->getMessage(), $this->level); + } + if ($this->mode & PEAR_ERROR_DIE) { + $msg = $this->getMessage(); + if (is_null($options) || is_int($options)) { + $format = "%s"; + if (substr($msg, -1) != "\n") { + $msg .= "\n"; + } + } else { + $format = $options; + } + die(sprintf($format, $msg)); + } + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_callable($this->callback)) { + call_user_func($this->callback, $this); + } + } + if ($this->mode & PEAR_ERROR_EXCEPTION) { + trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_ErrorStack for exceptions", E_USER_WARNING); + eval('$e = new Exception($this->message, $this->code);$e->PEAR_Error = $this;throw($e);'); + } + } + + // }}} + // {{{ getMode() + + /** + * Get the error mode from an error object. + * + * @return int error mode + * @access public + */ + function getMode() { + return $this->mode; + } + + // }}} + // {{{ getCallback() + + /** + * Get the callback function/method from an error object. + * + * @return mixed callback function or object/method array + * @access public + */ + function getCallback() { + return $this->callback; + } + + // }}} + // {{{ getMessage() + + + /** + * Get the error message from an error object. + * + * @return string full error message + * @access public + */ + function getMessage() + { + return ($this->error_message_prefix . $this->message); + } + + + // }}} + // {{{ getCode() + + /** + * Get error code from an error object + * + * @return int error code + * @access public + */ + function getCode() + { + return $this->code; + } + + // }}} + // {{{ getType() + + /** + * Get the name of this error/exception. + * + * @return string error/exception name (type) + * @access public + */ + function getType() + { + return get_class($this); + } + + // }}} + // {{{ getUserInfo() + + /** + * Get additional user-supplied information. + * + * @return string user-supplied information + * @access public + */ + function getUserInfo() + { + return $this->userinfo; + } + + // }}} + // {{{ getDebugInfo() + + /** + * Get additional debug information supplied by the application. + * + * @return string debug information + * @access public + */ + function getDebugInfo() + { + return $this->getUserInfo(); + } + + // }}} + // {{{ getBacktrace() + + /** + * Get the call backtrace from where the error was generated. + * Supported with PHP 4.3.0 or newer. + * + * @param int $frame (optional) what frame to fetch + * @return array Backtrace, or NULL if not available. + * @access public + */ + function getBacktrace($frame = null) + { + if ($frame === null) { + return $this->backtrace; + } + return $this->backtrace[$frame]; + } + + // }}} + // {{{ addUserInfo() + + function addUserInfo($info) + { + if (empty($this->userinfo)) { + $this->userinfo = $info; + } else { + $this->userinfo .= " ** $info"; + } + } + + // }}} + // {{{ toString() + + /** + * Make a string representation of this object. + * + * @return string a string with an object summary + * @access public + */ + function toString() { + $modes = array(); + $levels = array(E_USER_NOTICE => 'notice', + E_USER_WARNING => 'warning', + E_USER_ERROR => 'error'); + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_array($this->callback)) { + $callback = (is_object($this->callback[0]) ? + strtolower(get_class($this->callback[0])) : + $this->callback[0]) . '::' . + $this->callback[1]; + } else { + $callback = $this->callback; + } + return sprintf('[%s: message="%s" code=%d mode=callback '. + 'callback=%s prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + $callback, $this->error_message_prefix, + $this->userinfo); + } + if ($this->mode & PEAR_ERROR_PRINT) { + $modes[] = 'print'; + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + $modes[] = 'trigger'; + } + if ($this->mode & PEAR_ERROR_DIE) { + $modes[] = 'die'; + } + if ($this->mode & PEAR_ERROR_RETURN) { + $modes[] = 'return'; + } + return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. + 'prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + implode("|", $modes), $levels[$this->level], + $this->error_message_prefix, + $this->userinfo); + } + + // }}} +} + +/* + * Local Variables: + * mode: php + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ +?>