initial commit

This commit is contained in:
Chris Sewell
2012-11-28 03:55:08 -05:00
parent 7adb399b2e
commit cf140a2e97
3247 changed files with 492437 additions and 0 deletions

View File

@ -0,0 +1,135 @@
<?php
/*
* $Id: CallableStatement.php,v 1.7 2004/03/20 04:16:49 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/PreparedStatement.php';
/**
* Interface for callable statements.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.7 $
* @package creole
*/
interface CallableStatement extends PreparedStatement {
/**
* Register a parameter as an output param.
* @param string $paramIndex The stored procedure param name (e.g. @val1).
* @param int $sqlType The type of the parameter (e.g. Type::BIT)
* @param int $maxLength The maximum expected length (size) of output parameter.
*/
public function registerOutParameter($paramIndex, $sqlType, $maxLength = null);
/**
*
* @param mixed $paramIndex Parameter name (e.g. "@var1").
* @return array
* @throws SQLException if $paramIndex was not bound as output variable.
*/
public function getArray($paramIndex);
/**
*
* @param mixed $paramIndex Parameter name (e.g. "@var1").
* @return boolean
* @throws SQLException if $paramIndex was not bound as output variable.
*/
public function getBoolean($paramIndex);
/**
*
* @param mixed $paramIndex Parameter name (e.g. "@var1").
* @return Blob blob object
* @throws SQLException if $paramIndex was not bound as output variable.
*/
public function getBlob($paramIndex);
/**
* @param mixed $paramIndex Column name (string) or index (int).
* @return Clob clob object.
*/
public function getClob($paramIndex);
/**
* Return a formatted date.
*
* The default format for dates returned is preferred (in your locale, as specified using setlocale())
* format w/o time (i.e. strftime("%x", $val)). Override this by specifying a format second parameter. You
* can also specify a date()-style formatter; if you do, make sure there are no "%" symbols in your format string.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @param string $format Date formatter for use w/ strftime() or date() (it will choose based on examination of format string)
* If format is NULL, then the integer unix timestamp will be returned (no formatting performed).
* @return mixed Formatted date, or integer unix timestamp (using 00:00:00 for time) if $format was null.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getDate($column, $format = '%x');
/**
* @param mixed $paramIndex Column name (string) or index (int).
* @return float
*/
public function getFloat($paramIndex);
/**
* @param mixed $paramIndex Column name (string) or index (int).
* @return int
*/
public function getInt($paramIndex);
/**
* @param mixed $paramIndex Column name (string) or index (int).
* @return string
*/
public function getString($paramIndex);
/**
* Return a formatted time.
*
* The default format for times returned is preferred (in your locale, as specified using setlocale())
* format w/o date (i.e. strftime("%X", $val)). Override this by specifying a format second parameter. You
* can also specify a date()-style formatter; if you do, make sure there are no "%" symbols in your format string.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @param string $format Date formatter for use w/ strftime() or date() (it will choose based on examination of format string)
* If format is NULL, then the integer unix timestamp will be returned (no formatting performed).
* @return mixed Formatted time, or integer unix timestamp (using today's date) if $format was null.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getTime($column, $format = '%X');
/**
* Return a formatted timestamp.
*
* The default format for timestamp is ISO standard YYYY-MM-DD HH:MM:SS (i.e. date('Y-m-d H:i:s', $val).
* Override this by specifying a format second parameter. You can also specify a strftime()-style formatter.
*
* Hint: if you want to get the unix timestamp use the "U" formatter string.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @param string $format Date formatter for use w/ strftime() or date() (it will choose based on examination of format string)
* If format is NULL, then the integer unix timestamp will be returned (no formatting performed).
* @return mixed Formatted timestamp, or integer unix timestamp (if $format was null)
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getTimestamp($column, $format = 'Y-m-d H:i:s');
}

220
lib/symfony/vendor/creole/Connection.php vendored Executable file
View File

@ -0,0 +1,220 @@
<?php
/*
* $Id: Connection.php,v 1.29 2005/10/17 19:03:50 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
include_once 'creole/ResultSet.php'; // we need this for the fetchmode ResultSet flags (constants) that are passed to executeQuery()
/**
* Connection is an abstract base class for DB dialect implementations, and must be
* inherited by all such.
*
* Developer notes:
* (1) Make sure that your Connection class can be serialized. See the ConnectionCommon __sleep() and __wakeup() implimentation.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.29 $
* @package creole
*/
interface Connection {
// Constants that define transaction isolation levels.
// [We don't have any code using these yet, so there's no need
// to initialize these values at this point.]
// const TRANSACTION_NONE = 0;
// const TRANSACTION_READ_UNCOMMITTED = 1;
// const TRANSACTION_READ_COMMITTED = 2;
// const TRANSACTION_REPEATABLE_READ = 3;
// const TRANSACTION_SERIALIZABLE = 4;
/**
* Connect to a database and log in as the specified user.
*
* @param array $dsn The PEAR-style data source hash.
* @param int $flags (optional) Flags for connection (e.g. Creole::PERSISTENT). These flags
* may apply to any of the driver classes.
*/
public function connect($dsn, $flags = false);
/**
* Get the PHP native resource for the database connection/link.
* @return resource
*/
public function getResource();
/**
* Get any flags that were passed to connection.
* @return int
*/
public function getFlags();
/**
* Get the DSN array used by connect() method to connect to database.
* @see connect()
* @return array
*/
public function getDSN();
/**
* Gets a DatabaseInfo class for the current database.
*
* This is not modeled on the JDBC MetaData class, but provides a possibly more
* useful metadata system. All the same, there may eventually be a getMetaData()
* which returns a class that behaves like JDBC's DatabaseMetaData.
*
* @return DatabaseInfo
*/
public function getDatabaseInfo();
/**
* Loads and returns an IdGenerator object for current RDBMS.
* @return IdGenerator
*/
public function getIdGenerator();
/**
* Prepares a query for multiple execution with execute().
*
* With some database backends, this is emulated.
* prepare() requires a generic query as string like
* "INSERT INTO numbers VALUES(?,?,?)". The ? are placeholders.
*
* IMPORTANT: All occurrences of the placeholder (?) will be assumed
* to be a parameter. Therefore be sure not to have ? anywhere else in
* the query.
*
* So, ... DO NOT MIX WILDCARDS WITH ALREADY-PREPARED QUERIES
*
* INCORRECT:
* SELECT * FROM mytable WHERE id = ? AND title = 'Where are you?' and body LIKE ?
*
* CORRECT:
* SELECT * FROM mytable WHERE id = ? AND title = ? and body LIKE ?
*
* @param string $sql The query to prepare.
* @return PreparedStatement
* @throws SQLException
* @see PreparedStatement::execute()
*/
public function prepareStatement($sql);
/**
* Creates a new empty Statement.
* @return Statement
*/
public function createStatement();
/**
* If RDBMS supports native LIMIT/OFFSET then query SQL is modified
* so that no emulation is performed in ResultSet.
*
* @param string &$sql The query that will be modified.
* @param int $offset
* @param int $limit
* @return void
* @throws SQLException - if unable to modify query for any reason.
*/
public function applyLimit(&$sql, $offset, $limit);
/**
* Executes the SQL query in this PreparedStatement object and returns the resultset.
*
* @param string $sql The SQL statement.
* @param int $fetchmode
* @return object ResultSet
* @throws SQLException if a database access error occurs.
*/
public function executeQuery($sql, $fetchmode = null);
/**
* Executes the SQL INSERT, UPDATE, or DELETE statement.
*
* @param string $sql This method may optionally be called with the SQL statement.
* @return int Number of affected rows (or 0 for drivers that return nothing).
* @throws SQLException if a database access error occurs.
*/
public function executeUpdate($sql);
/**
* Creates a CallableStatement object for calling database stored procedures.
*
* @param string $sql
* @return CallableStatement
*/
public function prepareCall($sql);
/**
* Free the db resources.
* @return void
*/
public function close();
/**
* Returns false if connection is closed.
* @return boolean
*/
public function isConnected();
/**
* Get auto-commit status.
*
* @return boolean
*/
public function getAutoCommit();
/**
* Enable/disable automatic commits.
*
* Pushes SQLWarning onto $warnings stack if the autocommit value is being changed mid-transaction. This function
* is overridden by driver classes so that they can perform the necessary begin/end transaction SQL.
*
* If auto-commit is being set to TRUE, then the current transaction will be committed immediately.
*
* @param boolean $bit New value for auto commit.
* @return void
*/
public function setAutoCommit($bit);
/**
* Begins a transaction (if supported).
*
*/
public function begin();
/**
* Commits statements in a transaction.
*
*/
public function commit();
/**
* Rollback changes in a transaction.
*
*/
public function rollback();
/**
* Gets the number of rows affected by the data manipulation
* query.
*
* @return int Number of rows affected by the last query.
*/
public function getUpdateCount();
}

377
lib/symfony/vendor/creole/Creole.php vendored Executable file
View File

@ -0,0 +1,377 @@
<?php
/*
* $Id: Creole.php,v 1.14 2006/01/17 20:06:31 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
include_once 'creole/SQLException.php';
include_once 'creole/Connection.php';
// static:
// track errors is used by drivers to get better error messages
// make sure it's set.
@ini_set('track_errors', true);
/**
* This is the class that manages the database drivers.
*
* There are a number of default drivers (at the time of writing this comment: MySQL, MSSQL, SQLite, PgSQL, Oracle)
* that are "shipped" with Creole. You may wish to either add a new driver or swap out one of the existing drivers
* for your own custom driver. To do this you simply need to register your driver using the registerDriver() method.
*
* Note that you register your Connection class because the Connection class is responsible for calling the other
* driver classes (e.g. ResultSet, PreparedStatement, etc.).
*
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.14 $
* @package creole
*/
class Creole {
/**
* Constant that indicates a connection object should be used.
*/
const PERSISTENT = 1;
/**
* Flag to pass to the connection to indicate that no case conversions
* should be performed by ResultSet on keys of fetched rows.
* @deprecated use COMPAT_ASSOC_LOWER
*/
const NO_ASSOC_LOWER = 16;
/**
* Flag to pass to the connection to indicate that a to-lower case conversion
* should be performed by ResultSet on keys of fetched rows.
*/
const COMPAT_ASSOC_LOWER = 32;
/**
* Flag to pass to the connection to indicate that an rtrim() should be performed
* on strings (using ResultSet->getString(), etc.).
*/
const COMPAT_RTRIM_STRING = 64;
/**
* Flag to indicate that all compatibility flags should be set.
*/
const COMPAT_ALL = 96;
/**
* Map of built-in drivers.
* Change or add your own using registerDriver()
* @see registerDriver()
* @var array Hash mapping phptype => driver class (in dot-path notation, e.g. 'mysql' => 'creole.drivers.mysql.MySQLConnection').
*/
private static $driverMap = array( 'mysql' => 'creole.drivers.mysql.MySQLConnection',
'mysqli' => 'creole.drivers.mysqli.MySQLiConnection',
'pgsql' => 'creole.drivers.pgsql.PgSQLConnection',
'sqlite' => 'creole.drivers.sqlite.SQLiteConnection',
'oracle' => 'creole.drivers.oracle.OCI8Connection',
'mssql' => 'creole.drivers.mssql.MSSQLConnection',
'odbc' => 'creole.drivers.odbc.ODBCConnection'
);
/**
* Map of already established connections
* @see getConnection()
* @var array Hash mapping connection DSN => Connection instance
*/
private static $connectionMap = array();
/**
* Register your own RDBMS driver class.
*
* You can use this to specify your own class that replaces a default driver or
* adds support for a new driver. Register your own class by specifying the
* 'phptype' (e.g. mysql) and a dot-path notation to where your Connection class is
* relative to any location on the include path. You can also specify '*' as the phptype
* if you want to register a driver that will handle any native type (e.g. if creating
* a set of decorator classes that log SQL before calling native driver methods). YOU CAN
* ONLY REGISTER ONE CATCHALL ('*') DRIVER.
* <p>
* Note: the class you need to register is your Connection class because this is the
* class that's responsible for instantiating the other classes that are part of your
* driver. It is possible to mix & match drivers -- i.e. to write a custom driver where
* the Connection object just instantiates stock classes for ResultSet and PreparedStatement.
* Note that if you wanted to "override" only the ResultSet class you would also have to override
* the Connection and PreparedStatement classes so that they would return the correct ResultSet
* class. In the future we may implement a more "packaged" approach to drivers; for now we
* want to keep it simple.
*
* @param string $phptype The phptype (mysql, mssql, etc.). This is first part of DSN URL (e.g. mysql://localhost/...).
* You may also specify '*' to register a driver that will "wrap" the any native drivers.
* @param string $dotpath A dot-path locating your class. For example 'creole.drivers.mssql.MSSQLConnection'
* will be included like: include 'creole/drivers/mssql/MSSQLConnection.php' and the
* classname will be assumed to be 'MSSQLConnection'.
* @return void
*/
public static function registerDriver($phptype, $dotpath)
{
self::$driverMap[$phptype] = $dotpath;
}
/**
* Removes the driver for a PHP type. Note that this will remove user-registered
* drivers _and_ the default drivers.
* @param string $phptype The PHP type for driver to de-register.
* @see registerDriver()
*/
public static function deregisterDriver($phptype)
{
unset(self::$driverMap[$phptype]);
}
/**
* Returns the class path to the driver registered for specified type.
* @param string $phptype The phptype handled by driver (e.g. 'mysql', 'mssql', '*').
* @return string The driver class in dot-path notation (e.g. creole.drivers.mssql.MSSQLConnection)
* or NULL if no registered driver found.
*/
public static function getDriver($phptype)
{
if (isset(self::$driverMap[$phptype])) {
return self::$driverMap[$phptype];
} else {
return null;
}
}
/**
* Create a new DB connection object and connect to the specified
* database
*
* @param mixed $dsn "data source name", see the self::parseDSN
* method for a description of the dsn format. Can also be
* specified as an array of the format returned by DB::parseDSN().
* @param int $flags Connection flags (e.g. PERSISTENT).
*
* @return Connection Newly created DB connection object
* @throws SQLException
* @see self::parseDSN()
*/
public static function getConnection($dsn, $flags = 0)
{
if (is_array($dsn)) {
$dsninfo = $dsn;
} else {
$dsninfo = self::parseDSN($dsn);
}
// gather any flags from the DSN
if ( isset ( $dsninfo['persistent'] ) && ! empty ( $dsninfo['persistent'] ) )
$flags |= Creole::PERSISTENT;
if ( isset ( $dsninfo['compat_assoc_lower'] ) && ! empty ( $dsninfo['compat_assoc_lower'] ) )
$flags |= Creole::COMPAT_ASSOC_LOWER;
if ( isset ( $dsninfo['compat_rtrim_string'] ) && ! empty ( $dsninfo['compat_rtrim_string'] ) )
$flags |= Creole::COMPAT_RTRIM_STRING;
if ( isset ( $dsninfo['compat_all'] ) && ! empty ( $dsninfo['compat_all'] ) )
$flags |= Creole::COMPAT_ALL;
if ($flags & Creole::NO_ASSOC_LOWER) {
trigger_error("The Creole::NO_ASSOC_LOWER flag has been deprecated, and is now the default behavior. Use Creole::COMPAT_ASSOC_LOWER to lowercase resulset keys.", E_USER_WARNING);
}
// sort $dsninfo by keys so the serialized result is always the same
// for identical connection parameters, no matter what their order is
ksort($dsninfo);
$connectionMapKey = crc32(serialize($dsninfo + array('compat_flags' => ($flags & Creole::COMPAT_ALL))));
// see if we already have a connection with these parameters cached
if(isset(self::$connectionMap[$connectionMapKey]))
{
// persistent connections will be used if a non-persistent one was requested and is available
// but a persistent connection will be created if a non-persistent one is present
// TODO: impliment auto close of non persistent and replacing the
// non persistent with the persistent object so as we dont have
// both links open for no reason
if( isset(self::$connectionMap[$connectionMapKey][1]) ) { // is persistent
// a persistent connection with these parameters is already there,
// so we return it, no matter what was specified as persistent flag
$con = self::$connectionMap[$connectionMapKey][1];
} else {
// we don't have a persistent connection, and since the persistent
// flag wasn't set either, we just return the non-persistent connection
$con = self::$connectionMap[$connectionMapKey][0];
}
// if we're here, a non-persistent connection was already there, but
// the user wants a persistent one, so it will be created
if ($con->isConnected())
return $con;
}
// support "catchall" drivers which will themselves handle the details of connecting
// using the proper RDBMS driver.
if (isset(self::$driverMap['*'])) {
$type = '*';
} else {
$type = $dsninfo['phptype'];
if (!isset(self::$driverMap[$type])) {
throw new SQLException("No driver has been registered to handle connection type: $type");
}
}
// may need to make this more complex if we add support
// for 'dbsyntax'
$clazz = self::import(self::$driverMap[$type]);
$obj = new $clazz();
if (!($obj instanceof Connection)) {
throw new SQLException("Class does not implement creole.Connection interface: $clazz");
}
try {
$obj->connect($dsninfo, $flags);
} catch(SQLException $sqle) {
$sqle->setUserInfo($dsninfo);
throw $sqle;
}
$persistent = ($flags & Creole::PERSISTENT) === Creole::PERSISTENT;
return self::$connectionMap[$connectionMapKey][(int)$persistent] = $obj;
}
/**
* Parse a data source name.
*
* This isn't quite as powerful as DB::parseDSN(); it's also a lot simpler, a lot faster,
* and many fewer lines of code.
*
* A array with the following keys will be returned:
* phptype: Database backend used in PHP (mysql, odbc 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
*
* The format of the supplied DSN is in its fullest form:
*
* phptype://username:password@protocol+hostspec/database
*
* Most variations are allowed:
*
* phptype://username:password@protocol+hostspec:110//usr/db_file.db
* phptype://username:password@hostspec/database_name
* phptype://username:password@hostspec
* phptype://username@hostspec
* phptype://hostspec/database
* phptype://hostspec
* phptype
*
* @param string $dsn Data Source Name to be parsed
* @return array An associative array
*/
public static function parseDSN($dsn)
{
if (is_array($dsn)) {
return $dsn;
}
$parsed = array(
'phptype' => null,
'username' => null,
'password' => null,
'protocol' => null,
'hostspec' => null,
'port' => null,
'socket' => null,
'database' => null
);
$info = parse_url($dsn);
if (count($info) === 1) { // if there's only one element in result, then it must be the phptype
$parsed['phptype'] = array_pop($info);
return $parsed;
}
// some values can be copied directly
$parsed['phptype'] = @$info['scheme'];
$parsed['username'] = @$info['user'];
$parsed['password'] = @$info['pass'];
$parsed['port'] = @$info['port'];
$host = @$info['host'];
if (false !== ($pluspos = strpos($host, '+'))) {
$parsed['protocol'] = substr($host,0,$pluspos);
if ($parsed['protocol'] === 'unix') {
$parsed['socket'] = substr($host,$pluspos+1);
} else {
$parsed['hostspec'] = substr($host,$pluspos+1);
}
} else {
$parsed['hostspec'] = $host;
}
if (isset($info['path'])) {
$parsed['database'] = substr($info['path'], 1); // remove first char, which is '/'
}
if (isset($info['query'])) {
$opts = explode('&', $info['query']);
foreach ($opts as $opt) {
list($key, $value) = explode('=', $opt);
if (!isset($parsed[$key])) { // don't allow params overwrite
$parsed[$key] = urldecode($value);
}
}
}
return $parsed;
}
/**
* Include once a file specified in DOT notation.
* Package notation is expected to be relative to a location
* on the PHP include_path.
* @param string $class
* @return string unqualified classname
* @throws SQLException - if class does not exist and cannot load file
* - if after loading file class still does not exist
*/
public static function import($class) {
$pos = strrpos($class, '.');
// get just classname ('path.to.ClassName' -> 'ClassName')
if ($pos !== false) {
$classname = substr($class, $pos + 1);
}
else
{
$classname = $class;
}
if (!class_exists($classname, false)) {
$path = strtr($class, '.', DIRECTORY_SEPARATOR) . '.php';
$ret = include_once($path);
if ($ret === false) {
throw new SQLException("Unable to load driver class: " . $class);
}
if (!class_exists($classname)) {
throw new SQLException("Unable to find loaded class: $classname (Hint: make sure classname matches filename)");
}
}
return $classname;
}
}

187
lib/symfony/vendor/creole/CreoleTypes.php vendored Executable file
View File

@ -0,0 +1,187 @@
<?php
/*
* $Id: CreoleTypes.php,v 1.18 2005/11/07 22:38:52 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Generic Creole types modeled on JDBC types.
*
* @author David Giffin <david@giffin.org>
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.18 $
* @package creole
*/
abstract class CreoleTypes {
const BOOLEAN = 1;
const BIGINT = 2;
const SMALLINT = 3;
const TINYINT = 4;
const INTEGER = 5;
const CHAR = 6;
const VARCHAR = 7;
const TEXT = 17;
const FLOAT = 8;
const DOUBLE = 9;
const DATE = 10;
const TIME = 11;
const TIMESTAMP = 12;
const VARBINARY = 13;
const NUMERIC = 14;
const BLOB = 15;
const CLOB = 16;
const LONGVARCHAR = 17;
const DECIMAL = 18;
const REAL = 19;
const BINARY = 20;
const LONGVARBINARY = 21;
const YEAR = 22;
/** this is "ARRAY" from JDBC types */
const ARR = 23;
const OTHER = -1;
/** Map of Creole type integers to the setter/getter affix. */
protected static $affixMap = array(
self::BOOLEAN => 'Boolean',
self::BIGINT => 'String',
self::CHAR => 'String',
self::DATE => 'Date',
self::DOUBLE => 'Float',
self::FLOAT => 'Float',
self::INTEGER => 'Int',
self::SMALLINT => 'Int',
self::TINYINT => 'Int',
self::TIME => 'Time',
self::TIMESTAMP => 'Timestamp',
self::VARCHAR => 'String',
self::VARBINARY => 'Blob',
self::NUMERIC => 'Float',
self::BLOB => 'Blob',
self::CLOB => 'Clob',
self::LONGVARCHAR => 'String',
self::DECIMAL => 'Float',
self::REAL => 'Float',
self::BINARY => 'Blob',
self::LONGVARBINARY => 'Blob',
self::YEAR => 'Int',
self::ARR => 'Array',
self::OTHER => '', // get() and set() for unknown
);
/** Map of Creole type integers to their textual name. */
protected static $creoleTypeMap = array(
self::BOOLEAN => 'BOOLEAN',
self::BIGINT => 'BIGINT',
self::SMALLINT => 'SMALLINT',
self::TINYINT => 'TINYINT',
self::INTEGER => 'INTEGER',
self::NUMERIC => 'NUMERIC',
self::DECIMAL => 'DECIMAL',
self::REAL => 'REAL',
self::FLOAT => 'FLOAT',
self::DOUBLE => 'DOUBLE',
self::CHAR => 'CHAR',
self::VARCHAR => 'VARCHAR',
self::TEXT => 'TEXT',
self::TIME => 'TIME',
self::TIMESTAMP => 'TIMESTAMP',
self::DATE => 'DATE',
self::YEAR => 'YEAR',
self::VARBINARY => 'VARBINARY',
self::BLOB => 'BLOB',
self::CLOB => 'CLOB',
self::LONGVARCHAR => 'LONGVARCHAR',
self::BINARY => 'BINARY',
self::LONGVARBINARY => 'LONGVARBINARY',
self::ARR => 'ARR',
self::OTHER => 'OTHER', // string is "raw" return
);
/**
* This method returns the generic Creole (JDBC-like) type
* when given the native db type.
* @param string $nativeType DB native type (e.g. 'TEXT', 'byetea', etc.).
* @return int Creole native type (e.g. Types::LONGVARCHAR, Types::BINARY, etc.).
*/
public static function getType($nativeType) {
throw new Exception('This method must be overridden in subclasses!'); // abstract static not allowed since PHP 5.2
}
/**
* This method will return a native type that corresponds to the specified
* Creole (JDBC-like) type.
* If there is more than one matching native type, then the LAST defined
* native type will be returned.
* @return string Native type string.
*/
public static function getNativeType($creoleType) {
throw new Exception('This method must be overridden in subclasses!'); // abstract static not allowed since PHP 5.2
}
/**
* Gets the "affix" to use for ResultSet::get*() and PreparedStatement::set*() methods.
* <code>
* $setter = 'set' . CreoleTypes::getAffix(CreoleTypes::INTEGER);
* $stmt->$setter(1, $intval);
* // or
* $getter = 'get' . CreoleTypes::getAffix(CreoleTypes::TIMESTAMP);
* $timestamp = $rs->$getter();
* </code>
* @param int $creoleType The Creole types.
* @return string The default affix for getting/setting cols of this type.
* @throws SQLException if $creoleType does not correspond to an affix
*/
public static function getAffix($creoleType)
{
if (!isset(self::$affixMap[$creoleType])) {
$e = new SQLException("Unable to return 'affix' for unknown CreoleType: " . $creoleType);
throw $e;
}
return self::$affixMap[$creoleType];
}
/**
* Given the integer type, this method will return the corresponding type name.
* @param int $creoleType the integer Creole type.
* @return string The name of the Creole type (e.g. 'VARCHAR').
*/
public static function getCreoleName($creoleType)
{
if (!isset(self::$creoleTypeMap[$creoleType])) {
return null;
}
return self::$creoleTypeMap[$creoleType];
}
/**
* Given the name of a type (e.g. 'VARCHAR') this method will return the corresponding integer.
* @param string $creoleTypeName The case-sensisive (must be uppercase) name of the Creole type (e.g. 'VARCHAR').
* @return int the Creole type.
*/
public static function getCreoleCode($creoleTypeName)
{
$type = array_search($creoleTypeName, self::$creoleTypeMap);
if ($type === false) {
return null;
}
return $type;
}
}

57
lib/symfony/vendor/creole/IdGenerator.php vendored Executable file
View File

@ -0,0 +1,57 @@
<?php
/**
* Interface for classes that provide functionality to get SEQUENCE or AUTO-INCREMENT ids from the database.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.3 $
* @package creole
*/
interface IdGenerator {
/** SEQUENCE id generator type */
const SEQUENCE = 1;
/** AUTO INCREMENT id generator type */
const AUTOINCREMENT = 2;
/**
* Convenience method that returns TRUE if id is generated
* before an INSERT statement. This is the same as checking
* whether the generator type is SEQUENCE.
* @return boolean TRUE if gen id method is SEQUENCE
* @see getIdMethod()
*/
public function isBeforeInsert();
/**
* Convenience method that returns TRUE if id is generated
* after an INSERT statement. This is the same as checking
* whether the generator type is AUTOINCREMENT.
* @return boolean TRUE if gen id method is AUTOINCREMENT
* @see getIdMethod()
*/
public function isAfterInsert();
/**
* Get the preferred type / style for generating ids for RDBMS.
* @return int SEQUENCE or AUTOINCREMENT
*/
public function getIdMethod();
/**
* Get the autoincrement or sequence id given the current connection
* and any additional needed info (e.g. sequence name for sequences).
* <p>
* Note: if you take advantage of the fact that $keyInfo may not be specified
* you should make sure that your code is setup in such a way that it will
* be portable if you change from an RDBMS that uses AUTOINCREMENT to one that
* uses SEQUENCE (i.e. in which case you would need to specify sequence name).
*
* @param mixed $keyInfo Any additional information (e.g. sequence name) needed to fetch the id.
* @return int The last id / next id.
*/
public function getId($keyInfo = null);
}

View File

@ -0,0 +1,253 @@
<?php
/*
* $Id: PreparedStatement.php,v 1.21 2005/03/29 16:56:09 gamr Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Interface for a pre-compiled SQL statement.
*
* Many drivers do not take advantage of pre-compiling SQL statements; for these
* cases the precompilation is emulated. This emulation comes with slight penalty involved
* in parsing the queries, but provides other benefits such as a cleaner object model and ability
* to work with BLOB and CLOB values w/o needing special LOB-specific routines.
*
* This class is abstract because there are driver-specific implementations in [clearly] how queries
* are executed, and how parameters are bound.
*
* This class is not as abstract as the JDBC version. For exmple, if you are using a driver
* that uses name-based query param substitution, then you'd better bind your variables to
* names rather than index numbers. e.g. in Oracle
* <code>
* $stmt = $conn->prepareStatement("INSERT INTO users (name, passwd) VALUES (:name, :pass)");
* $stmt->setString(":name", $name);
* $stmt->executeUpdate();
* </code>
*
* Developer note: In many ways this interface is an extension of the Statement interface. However, due
* to limitations in PHP5's interface extension model (specifically that you cannot change signatures on
* methods defined in parent interface), we cannot extend the Statement interface.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.21 $
* @package creole
*/
interface PreparedStatement {
/**
* Gets the db Connection that created this statement.
* @return Connection
*/
public function getConnection();
/**
* Get the PHP native resource for the statement (if supported).
* @return resource
*/
public function getResource();
/**
* Free resources associated with this statement.
* Some drivers will need to implement this method to free
* database result resources.
*
* @return void
*/
public function close();
/**
* Get result set.
* This assumes that the last thing done was an executeQuery() or an execute()
* with SELECT-type query.
*
* @return RestultSet Last ResultSet or <code>null</code> if not applicable.
*/
public function getResultSet();
/**
* Gets next result set (if this behavior is supported by driver).
* Some drivers (e.g. MSSQL) support returning multiple result sets -- e.g.
* from stored procedures.
*
* This function also closes any current restult set.
*
* Default behavior is for this function to return false. Driver-specific
* implementations of this class can override this method if they actually
* support multiple result sets.
*
* @return boolean True if there is another result set, otherwise false.
*/
public function getMoreResults();
/**
* Get update count.
*
* @return int Number of records affected, or <code>null</code> if not applicable.
*/
public function getUpdateCount();
/**
* Sets the maximum number of rows to return from db.
* This will affect the SQL if the RDBMS supports native LIMIT; if not,
* it will be emulated. Limit only applies to queries (not update sql).
* @param int $v Maximum number of rows or 0 for all rows.
* @return void
*/
public function setLimit($v);
/**
* Returns the maximum number of rows to return or 0 for all.
* @return int
*/
public function getLimit();
/**
* Sets the start row.
* This will affect the SQL if the RDBMS supports native OFFSET; if not,
* it will be emulated. Offset only applies to queries (not update) and
* only is evaluated when LIMIT is set!
* @param int $v
* @return void
*/
public function setOffset($v);
/**
* Returns the start row.
* Offset only applies when Limit is set!
* @return int
*/
public function getOffset();
/**
* Executes the SQL query in this PreparedStatement object and returns the resultset generated by the query.
* We support two signatures for this method:
* - $stmt->executeQuery(ResultSet::FETCHMODE_NUM);
* - $stmt->executeQuery(array($param1, $param2), ResultSet::FETCHMODE_NUM);
* @param mixed $p1 Either (array) Parameters that will be set using PreparedStatement::set() before query is executed or (int) fetchmode.
* @param int $fetchmode The mode to use when fetching the results (e.g. ResultSet::FETCHMODE_NUM, ResultSet::FETCHMODE_ASSOC).
* @return ResultSet
* @throws SQLException if a database access error occurs.
*/
public function executeQuery();
/**
* Executes the SQL INSERT, UPDATE, or DELETE statement in this PreparedStatement object.
*
* @param array $params Parameters that will be set using PreparedStatement::set() before query is executed.
* @return int Number of affected rows (or 0 for drivers that return nothing).
* @throws SQLException if a database access error occurs.
*/
public function executeUpdate($params = null);
/**
* A generic set method.
*
* You can use this if you don't want to concern yourself with the details. It involves
* slightly more overhead than the specific settesr, since it grabs the PHP type to determine
* which method makes most sense.
*
* @param int $paramIndex
* @param mixed $value
* @return void
* @throws SQLException
*/
public function set($paramIndex, $value);
/**
* Sets an array.
* Unless a driver-specific method is used, this means simply serializing
* the passed parameter and storing it as a string.
* @param int $paramIndex
* @param array $value
* @return void
*/
public function setArray($paramIndex, $value);
/**
* Sets a boolean value.
* Default behavior is true = 1, false = 0.
* @param int $paramIndex
* @param boolean $value
* @return void
*/
public function setBoolean($paramIndex, $value);
/**
* @param int $paramIndex
* @param mixed $blob Blob object or string containing data.
* @return void
*/
public function setBlob($paramIndex, $blob);
/**
* @param int $paramIndex
* @param mixed $clob Clob object or string containing data.
* @return void
*/
public function setClob($paramIndex, $clob);
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
public function setDate($paramIndex, $value);
/**
* @param int $paramIndex
* @param float $value
* @return void
*/
public function setFloat($paramIndex, $value);
/**
* @param int $paramIndex
* @param int $value
* @return void
*/
public function setInt($paramIndex, $value);
/**
* @param int $paramIndex
* @return void
*/
public function setNull($paramIndex);
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
public function setString($paramIndex, $value);
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
public function setTime($paramIndex, $value);
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
public function setTimestamp($paramIndex, $value);
}

380
lib/symfony/vendor/creole/ResultSet.php vendored Executable file
View File

@ -0,0 +1,380 @@
<?php
/*
* $Id: ResultSet.php,v 1.28 2006/01/17 19:44:38 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* This is the interface for classes the wrap db results.
*
* The get*() methods in this interface will format values before returning them. Note
* that if they will return null if the database returned NULL. If the requested column does
* not exist than an exception (SQLException) will be thrown.
*
* <code>
* $rs = $conn->executeQuery("SELECT MAX(stamp) FROM event", ResultSet::FETCHMODE_NUM);
* $rs->next();
*
* $max_stamp = $rs->getTimestamp(1, "d/m/Y H:i:s");
* // $max_stamp will be date string or null if no MAX(stamp) was found
*
* $max_stamp = $rs->getTimestamp("max(stamp)", "d/m/Y H:i:s");
* // will THROW EXCEPTION, because the resultset was fetched using numeric indexing
* // SQLException: Invalid resultset column: max(stamp)
* </code>
*
* This class implements SPL IteratorAggregate, so you may iterate over the database results
* using foreach():
* <code>
* foreach($rs as $row) {
* print_r($row); // row is assoc array returned by getRow()
* }
* </code>
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.28 $
* @package creole
*/
interface ResultSet extends IteratorAggregate {
/**
* Index result set by field name.
*/
const FETCHMODE_ASSOC = 1;
/**
* Index result set numerically.
*/
const FETCHMODE_NUM = 2;
/**
* Get the PHP native resource for the result.
* Arguably this should not be part of the interface: i.e. every driver should implement
* it if they have a result resource, but conceivably drivers could be created that do
* not. For now every single driver does have a "dblink" resource property, and other
* classes (e.g. ResultSet) need this info in order to get correct native errors. We'll
* leave it in for now, as it helps with driver development, with the caveat that it
* could be removed from the interface at a later point.
* @return resource Query result or NULL if not not applicable.
*/
public function getResource();
/**
* Sets the fetchmode used to retrieve results.
* Changing fetchmodes mid-result retrieval is supported (haven't encountered any drivers
* that don't support that yet).
* @param int $mode ResultSet::FETCHMODE_NUM or ResultSet::FETCHMODE_ASSOC (default).
* @return void
*/
public function setFetchmode($mode);
/**
* Gets the fetchmode used to retrieve results.
* @return int ResultSet::FETCHMODE_NUM or ResultSet::FETCHMODE_ASSOC (default).
*/
public function getFetchmode();
/**
* Whether assoc result keys get converted to lowercase for compatibility.
*
* This defaults to FALSE unless Creole::COMPAT_ASSOC_LOWER flag has been passed to connection.
* This property is read-only since it must be set when connection is created. The
* reason for this behavior is some drivers (e.g. SQLite) do the case conversions internally
* based on a PHP ini value; it would not be possible to change the behavior from the ResultSet
* (since query has already been executed).
*
* @return boolean
*/
public function isLowerAssocCase();
/**
* Moves the internal cursor to the next position and fetches the row at that position.
*
* @return boolean <tt>true</tt> if success, <tt>false</tt> if no next record.
* @throws SQLException on any driver-level errors.
*/
public function next();
/**
* Moves the internal cursor to the previous position and fetches the
* row at that position.
*
* @return boolean <tt>true</tt> if success, <tt>false</tt> if no previous record.
* @throws SQLException - if unable to move to previous position
* - if ResultSet doesn't support reverse scrolling
*/
public function previous();
/**
* Moves the cursor a relative number of rows, either positive or negative and fetches
* the row at that position.
*
* Attempting to move beyond the first/last row in the result set positions the cursor before/after
* the first/last row and issues a Warning. Calling relative(0) is valid, but does not change the cursor
* position.
*
* @param integer $offset
* @return boolean <tt>true</tt> if cursor is on a row, <tt>false</tt> otherwise.
* @throws SQLException - if unable to move to relative position
* - if rel pos is negative & ResultSet doesn't support reverse scrolling
*/
public function relative($offset);
/**
* Moves the cursor to an absolute cursor position and fetches the row at that position.
*
* Attempting to move beyond the first/last row in the result set positions the cursor before/after
* the first/last row and issues a Warning.
*
* @param integer $pos cursor position, first position is 1.
* @return boolean <tt>true</tt> if cursor is on a row, <tt>false</tt> otherwise.
* @throws SQLException - if unable to move to absolute position
* - if position is before current pos & ResultSet doesn't support reverse scrolling
*/
public function absolute($pos);
/**
* Moves cursor position WITHOUT FETCHING ROW AT THAT POSITION.
*
* Generally this method is for internal driver stuff (e.g. other methods like
* absolute() or relative() might call this and then call next() to get the row).
* This method is public to facilitate more advanced ResultSet scrolling tools
* -- e.g. cleaner implimentation of ResultSetIterator.
*
* Some drivers will emulate seek() and not allow reverse seek (Oracle).
*
* Seek is 0-based, but seek() is only for moving to the space _before_ the record
* that you want to read. I.e. if you seek(0) and then call next() you will have the
* first row (i.e. same as calling first() or absolute(1)).
*
* <strong>IMPORTANT: You cannot rely on the return value of this method to know whether a given
* record exists for reading. In some cases seek() will correctly return <code>false</code> if
* the position doesn't exist, but in other drivers the seek is not performed until the
* record is fetched. You can check the return value of absolute() if you need to know
* whether a specific rec position is valid.</strong>
*
* @param int $rownum The cursor pos to seek to.
* @return boolean true on success, false if unable to seek to specified record.
* @throws SQLException if trying to seek backwards with a driver that doesn't
* support reverse-scrolling
*/
public function seek($rownum);
/**
* Move cursor to beginning of recordset.
* @return boolean <tt>true</tt> on success or <tt>false</tt> if not found.
* @throws SQLException - if unable to move to first position
* - if not at first pos & ResultSet doesn't support reverse scrolling
*/
public function first();
/**
* Move cursor to end of recordset.
* @return boolean <tt>true</tt> on success or <tt>false</tt> if not found.
* @throws SQLException - if unable to move to last position
* - if unable to get num rows
*/
public function last();
/**
* Sets cursort to before first record. This does not actually seek(), but
* simply sets cursor pos to 0.
* This is useful for inserting a record before the first in the set, etc.
* @return void
*/
public function beforeFirst();
/**
* Sets cursort to after the last record. This does not actually seek(), but
* simply sets the cursor pos to last + 1.
* This [will be] useful for inserting a record after the last in the set,
* when/if Creole supports updateable ResultSets.
* @return void
*/
public function afterLast();
/**
* Checks whether cursor is after the last record.
* @return boolean
* @throws SQLException on any driver-level error.
*/
public function isAfterLast();
/**
* Checks whether cursor is before the first record.
* @return boolean
* @throws SQLException on any driver-level error.
*/
public function isBeforeFirst();
/**
* Returns the current cursor position.
* Cursor positions start at 0, but as soon as first row is fetched
* cursor position is 1. (so first row is 1)
* @return int
*/
public function getCursorPos();
/**
* Gets current fields (assoc array).
* @return array
*/
public function getRow();
/**
* Get the number of rows in a result set.
* @return int the number of rows
* @throws SQLException - if unable to get a rowcount.
*/
public function getRecordCount();
/**
* Frees the resources allocated for this result set.
* Also empties any internal field array so that any calls to
* get() method on closed ResultSet will result in "Invalid column" SQLException.
* @return void
*/
public function close();
/**
* A generic get method returns unformatted (=string) value.
* This returns the raw results from the database. Usually this will be a string, but some drivers
* also can return objects (lob descriptors, etc) in certain cases.
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used) (if ResultSet::FETCHMODE_NUM was used).
* @return mixed Usually expect a string.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function get($column);
/**
* Reads a column as an array.
* The value of the column is unserialized & returned as an array. The generic case of this function is
* very PHP-specific. Other drivers (e.g. Postgres) will format values into their native array format.
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @return array value or null if database returned null.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getArray($column);
/**
* Returns value translated to boolean.
* Default is to map 0 => false, 1 => true, but some database drivers may override this behavior.
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @return boolean value or null if database returned null.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getBoolean($column);
/**
* Returns Blob with contents of column value.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @return Blob New Blob with data from column or null if database returned null.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getBlob($column);
/**
* Returns Clob with contents of column value.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @return Clob New Clob object with data from column or null if database returned null.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getClob($column);
/**
* Return a formatted date.
*
* The default format for dates returned is preferred (in your locale, as specified using setlocale())
* format w/o time (i.e. strftime("%x", $val)). Override this by specifying a format second parameter. You
* can also specify a date()-style formatter; if you do, make sure there are no "%" symbols in your format string.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @param string $format Date formatter for use w/ strftime() or date() (it will choose based on examination of format string)
* If format is NULL, then the integer unix timestamp will be returned (no formatting performed).
* @return mixed Formatted date, or integer unix timestamp (using 00:00:00 for time) if $format was null.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getDate($column, $format = '%x');
/**
* Returns value cast as a float (in PHP this is same as double).
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @return float value or null if database returned null
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getFloat($column);
/**
* Returns value cast as integer.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @return int value or null if database returned null
* @see getInteger()
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getInt($column);
/**
* Returns value cast as string.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @return string value or null if database returned null
* @see get()
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getString($column);
/**
* Return a formatted time.
*
* The default format for times returned is preferred (in your locale, as specified using setlocale())
* format w/o date (i.e. strftime("%X", $val)). Override this by specifying a format second parameter. You
* can also specify a date()-style formatter; if you do, make sure there are no "%" symbols in your format string.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @param string $format Date formatter for use w/ strftime() or date() (it will choose based on examination of format string)
* If format is NULL, then the integer unix timestamp will be returned (no formatting performed).
* @return mixed Formatted time, or integer unix timestamp (using today's date) if $format was null.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getTime($column, $format = '%X');
/**
* Return a formatted timestamp.
*
* The default format for timestamp is ISO standard YYYY-MM-DD HH:MM:SS (i.e. date('Y-m-d H:i:s', $val).
* Override this by specifying a format second parameter. You can also specify a strftime()-style formatter.
*
* Hint: if you want to get the unix timestamp use the "U" formatter string.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @param string $format Date formatter for use w/ strftime() or date() (it will choose based on examination of format string)
* If format is NULL, then the integer unix timestamp will be returned (no formatting performed).
* @return mixed Formatted timestamp, or integer unix timestamp (if $format was null)
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getTimestamp($column, $format = 'Y-m-d H:i:s');
}

View File

@ -0,0 +1,113 @@
<?php
/*
* $Id: ResultSetIterator.php,v 1.3 2004/03/15 17:47:45 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Basic ResultSet Iterator.
*
* This can be returned by your class's getIterator() method, but of course
* you can also implement your own (e.g. to get better performance, by using direct
* driver calls and avoiding other side-effects inherent in ResultSet scrolling
* functions -- e.g. beforeFirst() / afterLast(), etc.).
*
* Important: ResultSet iteration does rewind the resultset if it is not at the
* start. Not all drivers support reverse scrolling, so this may result in an
* exception in some cases (Oracle).
*
* Developer note:
* The implementation of this class is a little weird because it fetches the
* array _early_ in order to answer valid() w/o needing to know total num
* of fields. Remember the way iterators work:
* <code>
* $it = $obj->getIterator();
* for($it->rewind(); $it->valid(); $it->next()) {
* $key = $it->current();
* $val = $it->key();
* echo "$key = $val\n";
* }
* unset($it);
* </code>
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.3 $
* @package creole
*/
class ResultSetIterator implements Iterator {
private $rs;
/**
* Construct the iterator.
* @param ResultSet $rs
*/
public function __construct(ResultSet $rs)
{
$this->rs = $rs;
}
/**
* If not at start of resultset, this method will call seek(0).
* @see ResultSet::seek()
*/
function rewind()
{
if (!$this->rs->isBeforeFirst()) {
$this->rs->seek(0);
}
}
/**
* This method checks to see whether there are more results
* by advancing the cursor position.
* @see ResultSet::next()
*/
function valid()
{
return $this->rs->next();
}
/**
* Returns the cursor position.
* @return int
*/
function key()
{
return $this->rs->getCursorPos();
}
/**
* Returns the row (assoc array) at current cursor pos.
* @return array
*/
function current()
{
return $this->rs->getRow();
}
/**
* This method does not actually do anything since we have already advanced
* the cursor pos in valid().
* @see valid()
*/
function next()
{
}
}

105
lib/symfony/vendor/creole/SQLException.php vendored Executable file
View File

@ -0,0 +1,105 @@
<?php
/*
* $Id: SQLException.php,v 1.10 2004/03/20 04:16:49 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* A class for handling database-related errors.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.10 $
* @package creole
*/
class SQLException extends Exception {
/** Information that provides additional information for context of Exception (e.g. SQL statement or DSN). */
protected $userInfo;
/** Native RDBMS error string */
protected $nativeError;
/**
* Constructs a SQLException.
* @param string $msg Error message
* @param string $native Native DB error message.
* @param string $userinfo More info, e.g. the SQL statement or the connection string that caused the error.
*/
public function __construct($msg, $native = null, $userinfo = null)
{
parent::__construct($msg);
if ($native !== null) {
$this->setNativeError($native);
}
if ($userinfo !== null) {
$this->setUserInfo($userinfo);
}
}
/**
* Sets additional user / debug information for this error.
*
* @param array $info
* @return void
*/
public function setUserInfo($info)
{
$this->userInfo = $info;
$this->message .= " [User Info: " .$this->userInfo . "]";
}
/**
* Returns the additional / debug information for this error.
*
* @return array hash of user info properties.
*/
public function getUserInfo()
{
return $this->userInfo;
}
/**
* Sets driver native error message.
*
* @param string $info
* @return void
*/
public function setNativeError($msg)
{
$this->nativeError = $msg;
$this->message .= " [Native Error: " .$this->nativeError . "]";
}
/**
* Gets driver native error message.
*
* @return string
*/
public function getNativeError()
{
return $this->nativeError;
}
/**
* @deprecated This method only exists right now for easier compatibility w/ PHPUnit!
*/
public function toString()
{
return $this->getMessage();
}
}

147
lib/symfony/vendor/creole/Statement.php vendored Executable file
View File

@ -0,0 +1,147 @@
<?php
/*
* $Id: Statement.php,v 1.17 2004/03/20 04:16:49 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Class that represents a SQL statement.
*
* This class is very generic and has no driver-specific implementations. In fact,
* it wouldn't be possible to have driver-specific classes, since PHP doesn't support
* multiple inheritance. I.e. you couldn't have MySQLPreparedStatement that extended
* both the abstract PreparedStatement class and the MySQLStatement class. In Java
* this isn't a concern since PreparedStatement is an interface, not a class.
*
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.17 $
* @package creole
*/
interface Statement {
/**
* Sets the maximum number of rows to return from db.
* This will affect the SQL if the RDBMS supports native LIMIT; if not,
* it will be emulated. Limit only applies to queries (not update sql).
* @param int $v Maximum number of rows or 0 for all rows.
* @return void
*/
public function setLimit($v);
/**
* Returns the maximum number of rows to return or 0 for all.
* @return int
*/
public function getLimit();
/**
* Sets the start row.
* This will affect the SQL if the RDBMS supports native OFFSET; if not,
* it will be emulated. Offset only applies to queries (not update) and
* only is evaluated when LIMIT is set!
* @param int $v
* @return void
*/
public function setOffset($v);
/**
* Returns the start row.
* Offset only applies when Limit is set!
* @return int
*/
public function getOffset();
/**
* Free resources associated with this statement.
* Some drivers will need to implement this method to free
* database result resources.
*
* @return void
*/
public function close();
/**
* Generic execute() function has to check to see whether SQL is an update or select query.
*
* If you already know whether it's a SELECT or an update (manipulating) SQL, then use
* the appropriate method, as this one will incurr overhead to check the SQL.
*
* @param int $fetchmode Fetchmode (only applies to queries).
* @return boolean True if it is a result set, false if not or if no more results (this is identical to JDBC return val).
* @throws SQLException
*/
public function execute($sql, $fetchmode = null);
/**
* Get result set.
* This assumes that the last thing done was an executeQuery() or an execute()
* with SELECT-type query.
*
* @return RestultSet (or null if none)
*/
public function getResultSet();
/**
* Get update count.
*
* @return int Number of records affected, or <code>null</code> if not applicable.
*/
public function getUpdateCount();
/**
* Executes the SQL query in this PreparedStatement object and returns the resultset generated by the query.
*
* @param string $sql This method may optionally be called with the SQL statement.
* @param int $fetchmode The mode to use when fetching the results (e.g. ResultSet::FETCHMODE_NUM, ResultSet::FETCHMODE_ASSOC).
* @return object Creole::ResultSet
* @throws SQLException if a database access error occurs.
*/
public function executeQuery($sql, $fetchmode = null);
/**
* Executes the SQL INSERT, UPDATE, or DELETE statement in this PreparedStatement object.
*
* @param string $sql This method may optionally be called with the SQL statement.
* @return int Number of affected rows (or 0 for drivers that return nothing).
* @throws SQLException if a database access error occurs.
*/
public function executeUpdate($sql);
/**
* Gets next result set (if this behavior is supported by driver).
* Some drivers (e.g. MSSQL) support returning multiple result sets -- e.g.
* from stored procedures.
*
* This function also closes any current restult set.
*
* Default behavior is for this function to return false. Driver-specific
* implementations of this class can override this method if they actually
* support multiple result sets.
*
* @return boolean True if there is another result set, otherwise false.
*/
public function getMoreResults();
/**
* Gets the db Connection that created this statement.
* @return Connection
*/
public function getConnection();
}

View File

@ -0,0 +1,258 @@
<?php
/*
* $Id: ConnectionCommon.php,v 1.5 2005/10/17 19:03:51 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Class that contains some shared/default information for connections. Classes may wish to extend this so
* as not to worry about the sleep/wakeup methods, etc.
*
* In reality this class is not very useful yet, so there's not much incentive for drivers to extend this.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.5 $
* @package creole.common
*/
abstract class ConnectionCommon {
// Constants that define transaction isolation levels.
// [We don't have any code using these yet, so there's no need
// to initialize these values at this point.]
// const TRANSACTION_NONE = 0;
// const TRANSACTION_READ_UNCOMMITTED = 1;
// const TRANSACTION_READ_COMMITTED = 2;
// const TRANSACTION_REPEATABLE_READ = 3;
// const TRANSACTION_SERIALIZABLE = 4;
/**
* The depth level of current transaction.
* @var int
*/
protected $transactionOpcount = 0;
/**
* DB connection resource id.
* @var resource
*/
protected $dblink;
/**
* Array hash of connection properties.
* @var array
*/
protected $dsn;
/**
* Flags (e.g. Connection::PERSISTENT) for current connection.
* @var int
*/
protected $flags = 0;
/**
* This "magic" method is invoked upon serialize() and works in tandem with the __wakeup()
* method to ensure that your database connection is serializable.
*
* This method returns an array containing the names of any members of your class
* which need to be serialized in order to allow the class to re-connect to the database
* when it is unserialized.
*
* <p>
* Developers:
*
* Note that you cannot serialize resources (connection links) and expect them to
* be valid when you unserialize. For this reason, you must re-connect to the database in the
* __wakeup() method.
*
* It's up to your class implimentation to ensure that the necessary data is serialized.
* You probably at least need to serialize:
*
* (1) the DSN array used by connect() method
* (2) Any flags that were passed to the connection
* (3) Possibly the autocommit state
*
* @return array The class variable names that should be serialized.
* @see __wakeup()
* @see DriverManager::getConnection()
* @see DatabaseInfo::__sleep()
*/
public function __sleep()
{
return array('dsn', 'flags');
}
/**
* This "magic" method is invoked upon unserialize().
* This method will re-connects to the database using the information that was
* stored using the __sleep() method.
* @see __sleep()
*/
public function __wakeup()
{
$this->connect($this->dsn, $this->flags);
}
/**
* @see Connection::getResource()
*/
public function getResource()
{
return $this->dblink;
}
/**
* @see Connection::getDSN()
*/
public function getDSN() {
return $this->dsn;
}
/**
* @see Connection::getFlags()
*/
public function getFlags()
{
return $this->flags;
}
/**
* Creates a CallableStatement object for calling database stored procedures.
*
* @param string $sql
* @return CallableStatement
*/
public function prepareCall($sql)
{
throw new SQLException("Current driver does not support stored procedures using CallableStatement.");
}
/**
* Driver classes should override this if they support transactions.
*
* @return boolean
*/
public function supportsNestedTrans()
{
return false;
}
/**
* Begins a transaction (if supported).
*/
public function begin()
{
if ($this->transactionOpcount === 0 || $this->supportsNestedTrans()) {
$this->beginTrans();
}
$this->transactionOpcount++;
}
/**
* Commits statements in a transaction.
*/
public function commit()
{
if ($this->transactionOpcount > 0) {
if ($this->transactionOpcount == 1 || $this->supportsNestedTrans()) {
$this->commitTrans();
}
$this->transactionOpcount--;
}
}
/**
* Rollback changes in a transaction.
*/
public function rollback()
{
if ($this->transactionOpcount > 0) {
if ($this->transactionOpcount == 1 || $this->supportsNestedTrans()) {
$this->rollbackTrans();
}
$this->transactionOpcount--;
}
}
/**
* Enable/disable automatic commits.
*
* Pushes SQLWarning onto $warnings stack if the autocommit value is being changed mid-transaction. This function
* is overridden by driver classes so that they can perform the necessary begin/end transaction SQL.
*
* If auto-commit is being set to TRUE, then the current transaction will be committed immediately.
*
* @param boolean $bit New value for auto commit.
* @return void
*/
public function setAutoCommit($bit)
{
if ($this->transactionOpcount > 0) {
trigger_error("Changing autocommit in mid-transaction; committing " . $this->transactionOpcount . " uncommitted statements.", E_USER_WARNING);
}
if (!$bit) {
$this->begin();
}
else {
$this->commit();
}
}
/**
* Get auto-commit status.
*
* @return boolean
*/
public function getAutoCommit()
{
return ($this->transactionOpcount == 0);
}
/**
* Begin new transaction.
* Driver classes should override this method if they support transactions.
*/
protected function beginTrans()
{
}
/**
* Commit the current transaction.
* Driver classes should override this method if they support transactions.
*/
protected function commitTrans()
{
}
/**
* Roll back (undo) the current transaction.
* Driver classes should override this method if they support transactions.
*/
protected function rollbackTrans()
{
}
/**
* Returns false if connection is closed.
* @return boolean
*/
public function isConnected()
{
return !empty($this->dblink);
}
}

View File

@ -0,0 +1,640 @@
<?php
/*
* $Id: PreparedStatementCommon.php,v 1.16 2005/11/13 01:30:00 gamr Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Class that represents a shared code for handling emulated pre-compiled statements.
*
* Many drivers do not take advantage of pre-compiling SQL statements; for these
* cases the precompilation is emulated. This emulation comes with slight penalty involved
* in parsing the queries, but provides other benefits such as a cleaner object model and ability
* to work with BLOB and CLOB values w/o needing special LOB-specific routines.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.16 $
* @package creole.common
*/
abstract class PreparedStatementCommon {
/**
* The database connection.
* @var Connection
*/
protected $conn;
/**
* Max rows to retrieve from DB.
* @var int
*/
protected $limit = 0;
/**
* Offset at which to start processing DB rows.
* "Skip X rows"
* @var int
*/
protected $offset = 0;
/**
* The SQL this class operates on.
* @var string
*/
protected $sql;
/**
* Possibly contains a cached prepared SQL Statement.
* Gives an early out to replaceParams if the same
* query is run multiple times without changing the
* params.
* @var string
*/
protected $sql_cache;
/**
* Flag to set if the cache is upto date or not
* @var boolean
*/
protected $sql_cache_valid = false;
/**
* The string positions of the parameters in the SQL.
* @var array
*/
protected $positions;
/**
* Number of positions (simply to save processing).
* @var int
*/
protected $positionsCount;
/**
* Map of index => value for bound params.
* @var array string[]
*/
protected $boundInVars = array();
/**
* Temporarily hold a ResultSet object after an execute() query.
* @var ResultSet
*/
protected $resultSet;
/**
* Temporary hold the affected row cound after an execute() query.
* @var int
*/
protected $updateCount;
/**
* Create new prepared statement instance.
*
* @param object $conn Connection object
* @param string $sql The SQL to work with.
* @param array $positions The positions in SQL of ?'s.
* @param restult $stmt If the driver supports prepared queries, then $stmt will contain the statement to use.
*/
public function __construct(Connection $conn, $sql)
{
$this->conn = $conn;
$this->sql = $sql;
$this->positions = $this->parseQuery ( $sql );
// save processing later in cases where we may repeatedly exec statement
$this->positionsCount = count ( $this->positions );
}
/**
* Parse the SQL query for ? positions
*
* @param string $sql The query to process
* @return array Positions from the start of the string that ?'s appear at
*/
protected function parseQuery ( $sql )
{
$positions = array();
// match anything ? ' " or \ in $sql with an early out if we find nothing
if ( preg_match_all ( '([\?]|[\']|[\"]|[\\\])', $sql, $matches, PREG_OFFSET_CAPTURE ) !== 0 ) {
$matches = $matches['0'];
$open = NULL;
// go thru all our matches and see what we can find
for ( $i = 0, $j = count ( $matches ); $i < $j; $i++ ) {
switch ( $matches[$i]['0'] ) {
// if we already have an open " or ' then check if this is the end
// to close it or not
case $open:
$open = NULL;
break;
// we have a quote, set ourselves open
case '"':
case "'":
$open = $matches[$i]['0'];
break;
// check if it is an escaped quote and skip if it is
case '\\':
$next_match = $matches[$i+1]['0'];
if ( $next_match === '"' || $next_match === "'" ) {
$i++;
}
unset ( $next_match );
break;
// we found a ?, check we arent in an open "/' first and
// add it to the position list if we arent
default:
if ( $open === NULL ) {
$positions[] = $matches[$i]['1'];
}
}
unset ( $matches[$i] );
}
unset ( $open, $matches, $i, $j );
}
return $positions;
}
/**
* @see PreparedStatement::setLimit()
*/
public function setLimit($v)
{
$this->limit = (int) $v;
}
/**
* @see PreparedStatement::getLimit()
*/
public function getLimit()
{
return $this->limit;
}
/**
* @see PreparedStatement::setOffset()
*/
public function setOffset($v)
{
$this->offset = (int) $v;
}
/**
* @see PreparedStatement::getOffset()
*/
public function getOffset()
{
return $this->offset;
}
/**
* @see PreparedStatement::getResultSet()
*/
public function getResultSet()
{
return $this->resultSet;
}
/**
* @see PreparedStatement::getUpdateCount()
*/
public function getUpdateCount()
{
return $this->updateCount;
}
/**
* @see PreparedStatement::getMoreResults()
*/
public function getMoreResults()
{
if ($this->resultSet) $this->resultSet->close();
$this->resultSet = null;
return false;
}
/**
* @see PreparedStatement::getConnection()
*/
public function getConnection()
{
return $this->conn;
}
/**
* Statement resources do not exist for emulated prepared statements,
* so this just returns <code>null</code>.
* @return null
*/
public function getResource()
{
return null;
}
/**
* Nothing to close for emulated prepared statements.
*/
public function close()
{
}
/**
* Replaces placeholders with the specified parameter values in the SQL.
*
* This is for emulated prepared statements.
*
* @return string New SQL statement with parameters replaced.
* @throws SQLException - if param not bound.
*/
protected function replaceParams()
{
// early out if we still have the same query ready
if ( $this->sql_cache_valid === true ) {
return $this->sql_cache;
}
// Default behavior for this function is to behave in 'emulated' mode.
$sql = '';
$last_position = 0;
for ($position = 0; $position < $this->positionsCount; $position++) {
if (!isset($this->boundInVars[$position + 1])) {
throw new SQLException('Replace params: undefined query param: ' . ($position + 1));
}
$current_position = $this->positions[$position];
$sql .= substr($this->sql, $last_position, $current_position - $last_position);
$sql .= $this->boundInVars[$position + 1];
$last_position = $current_position + 1;
}
// append the rest of the query
$sql .= substr($this->sql, $last_position);
// just so we dont touch anything with a blob/clob
if ( strlen ( $sql ) > 2048 ) {
$this->sql_cache = $sql;
$this->sql_cache_valid = true;
return $this->sql_cache;
} else {
return $sql;
}
}
/**
* Executes the SQL query in this PreparedStatement object and returns the resultset generated by the query.
* We support two signatures for this method:
* - $stmt->executeQuery(ResultSet::FETCHMODE_NUM);
* - $stmt->executeQuery(array($param1, $param2), ResultSet::FETCHMODE_NUM);
* @param mixed $p1 Either (array) Parameters that will be set using PreparedStatement::set() before query is executed or (int) fetchmode.
* @param int $fetchmode The mode to use when fetching the results (e.g. ResultSet::FETCHMODE_NUM, ResultSet::FETCHMODE_ASSOC).
* @return ResultSet
* @throws SQLException if a database access error occurs.
*/
public function executeQuery($p1 = null, $fetchmode = null)
{
$params = null;
if ($fetchmode !== null) {
$params = $p1;
} elseif ($p1 !== null) {
if (is_array($p1)) $params = $p1;
else $fetchmode = $p1;
}
foreach ( (array) $params as $i=>$param ) {
$this->set ( $i + 1, $param );
unset ( $i, $param );
}
unset ( $params );
$this->updateCount = null; // reset
$sql = $this->replaceParams();
if ($this->limit > 0 || $this->offset > 0) {
$this->conn->applyLimit($sql, $this->offset, $this->limit);
}
$this->resultSet = $this->conn->executeQuery($sql, $fetchmode);
return $this->resultSet;
}
/**
* Executes the SQL INSERT, UPDATE, or DELETE statement in this PreparedStatement object.
*
* @param array $params Parameters that will be set using PreparedStatement::set() before query is executed.
* @return int Number of affected rows (or 0 for drivers that return nothing).
* @throws SQLException if a database access error occurs.
*/
public function executeUpdate($params = null)
{
foreach ( (array) $params as $i=>$param ) {
$this->set ( $i + 1, $param );
unset ( $i, $param );
}
unset ( $params );
if($this->resultSet) $this->resultSet->close();
$this->resultSet = null; // reset
$sql = $this->replaceParams();
$this->updateCount = $this->conn->executeUpdate($sql);
return $this->updateCount;
}
/**
* Escapes special characters (usu. quotes) using native driver function.
* @param string $str The input string.
* @return string The escaped string.
*/
abstract protected function escape($str);
/**
* A generic set method.
*
* You can use this if you don't want to concern yourself with the details. It involves
* slightly more overhead than the specific settesr, since it grabs the PHP type to determine
* which method makes most sense.
*
* @param int $paramIndex
* @param mixed $value
* @return void
* @throws SQLException
*/
function set($paramIndex, $value)
{
$type = gettype($value);
if ($type == "object") {
if (is_a($value, 'Blob')) {
$this->setBlob($paramIndex, $value);
} elseif (is_a($value, 'Clob')) {
$this->setClob($paramIndex, $value);
} elseif (is_a($value, 'Date')) {
// can't be sure if the column type is a DATE, TIME, or TIMESTAMP column
// we'll just use TIMESTAMP by default; hopefully DB won't complain (if
// it does, then this method just shouldn't be used).
$this->setTimestamp($paramIndex, $value);
} else {
throw new SQLException("Unsupported object type passed to set(): " . get_class($value));
}
} else {
switch ( $type ) {
case 'integer':
$type = 'int';
break;
case 'double':
$type = 'float';
break;
}
$setter = 'set' . ucfirst($type); // PHP types are case-insensitive, but we'll do this in case that change
if ( method_exists ( $this, $setter ) ) {
$this->$setter($paramIndex, $value);
} else {
throw new SQLException ( "Unsupported datatype passed to set(): " . $type );
}
}
}
/**
* Sets an array.
* Unless a driver-specific method is used, this means simply serializing
* the passed parameter and storing it as a string.
* @param int $paramIndex
* @param array $value
* @return void
*/
function setArray($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
$this->boundInVars[$paramIndex] = "'" . $this->escape(serialize($value)) . "'";
}
}
/**
* Sets a boolean value.
* Default behavior is true = 1, false = 0.
* @param int $paramIndex
* @param boolean $value
* @return void
*/
function setBoolean($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
$this->boundInVars[$paramIndex] = (int) $value;
}
}
/**
* @see PreparedStatement::setBlob()
*/
function setBlob($paramIndex, $blob)
{
$this->sql_cache_valid = false;
if ($blob === null) {
$this->setNull($paramIndex);
} else {
// they took magic __toString() out of PHP5.0.0; this sucks
if (is_object($blob)) {
$this->boundInVars[$paramIndex] = "'" . $this->escape($blob->__toString()) . "'";
} else {
$this->boundInVars[$paramIndex] = "'" . $this->escape($blob) . "'";
}
}
}
/**
* @see PreparedStatement::setClob()
*/
function setClob($paramIndex, $clob)
{
$this->sql_cache_valid = false;
if ($clob === null) {
$this->setNull($paramIndex);
} else {
// they took magic __toString() out of PHP5.0.0; this sucks
if (is_object($clob)) {
$this->boundInVars[$paramIndex] = "'" . $this->escape($clob->__toString()) . "'";
} else {
$this->boundInVars[$paramIndex] = "'" . $this->escape($clob) . "'";
}
}
}
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
function setDate($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
if (is_numeric($value)) $value = date("Y-m-d", $value);
elseif (is_object($value)) $value = date("Y-m-d", $value->getTime());
$this->boundInVars[$paramIndex] = "'" . $this->escape($value) . "'";
}
}
/**
* @param int $paramIndex
* @param double $value
* @return void
*/
function setDecimal($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
$this->boundInVars[$paramIndex] = (float) $value;
}
}
/**
* @param int $paramIndex
* @param double $value
* @return void
*/
function setDouble($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
$this->boundInVars[$paramIndex] = (double) $value;
}
}
/**
* @param int $paramIndex
* @param float $value
* @return void
*/
function setFloat($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
$this->boundInVars[$paramIndex] = (float) $value;
}
}
/**
* @param int $paramIndex
* @param int $value
* @return void
*/
function setInt($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
$this->boundInVars[$paramIndex] = (int) $value;
}
}
/**
* Alias for setInt()
* @param int $paramIndex
* @param int $value
*/
function setInteger($paramIndex, $value)
{
$this->sql_cache_valid = false;
$this->setInt($paramIndex, $value);
}
/**
* @param int $paramIndex
* @return void
*/
function setNull($paramIndex)
{
$this->sql_cache_valid = false;
$this->boundInVars[$paramIndex] = 'NULL';
}
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
function setString($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
// it's ok to have a fatal error here, IMO, if object doesn't have
// __toString() and is being passed to this method.
if ( is_object ( $value ) ) {
$this->boundInVars[$paramIndex] = "'" . $this->escape($value->__toString()) . "'";
} else {
$this->boundInVars[$paramIndex] = "'" . $this->escape((string)$value) . "'";
}
}
}
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
function setTime($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
if ( is_numeric ( $value ) ) {
$value = date ('H:i:s', $value );
} elseif ( is_object ( $value ) ) {
$value = date ('H:i:s', $value->getTime ( ) );
}
$this->boundInVars [ $paramIndex ] = "'" . $this->escape ( $value ) . "'";
}
}
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
function setTimestamp($paramIndex, $value)
{
$this->sql_cache_valid = false;
if ($value === null) {
$this->setNull($paramIndex);
} else {
if (is_numeric($value)) $value = date('Y-m-d H:i:s', $value);
elseif (is_object($value)) $value = date('Y-m-d H:i:s', $value->getTime());
$this->boundInVars[$paramIndex] = "'".$this->escape($value)."'";
}
}
}

View File

@ -0,0 +1,447 @@
<?php
/*
* $Id: ResultSetCommon.php,v 1.9 2006/01/17 19:44:38 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* This class implements many shared or common methods needed by resultset drivers.
*
* This class may (optionally) be extended by driver classes simply to make it easier
* to create driver classes. This is also useful in the early stages of Creole development
* as it means that API changes affect fewer files. As Creole matures/stabalizes having
* a common class may become less useful, as drivers may have their own ways of doing things
* (and we'll have a solid unit test framework to make sure drivers conform to the API
* described by the interfaces).
*
* The get*() methods in this class will format values before returning them. Note
* that if they will return <code>null</code> if the database returned <code>NULL</code>
* which makes these functions easier to use than simply typecasting the values from the
* db. If the requested column does not exist than an exception (SQLException) will be thrown.
*
* <code>
* $rs = $conn->executeQuery("SELECT MAX(stamp) FROM event", ResultSet::FETCHMODE_NUM);
* $rs->next();
*
* $max_stamp = $rs->getTimestamp(1, "d/m/Y H:i:s");
* // $max_stamp will be date string or null if no MAX(stamp) was found
*
* $max_stamp = $rs->getTimestamp("max(stamp)", "d/m/Y H:i:s");
* // will THROW EXCEPTION, because the resultset was fetched using numeric indexing
* // SQLException: Invalid resultset column: max(stamp)
* </code>
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.9 $
* @package creole.common
*/
abstract class ResultSetCommon {
/**
* The fetchmode for this recordset.
* @var int
*/
protected $fetchmode;
/**
* DB connection.
* @var Connection
*/
protected $conn;
/**
* Resource identifier used for native result set handling.
* @var resource
*/
protected $result;
/**
* The current cursor position (row number). First row is 1. Before first row is 0.
* @var int
*/
protected $cursorPos = 0;
/**
* The current unprocessed record/row from the db.
* @var array
*/
protected $fields;
/**
* Whether to convert assoc col case.
* @var boolean
*/
protected $lowerAssocCase = false;
/**
* Whether to apply rtrim() to strings.
* @var boolean
*/
protected $rtrimString = false;
/**
* Constructor.
*/
public function __construct(Connection $conn, $result, $fetchmode = null)
{
$this->conn = $conn;
$this->result = $result;
if ($fetchmode !== null) {
$this->fetchmode = $fetchmode;
} else {
$this->fetchmode = ResultSet::FETCHMODE_ASSOC; // default
}
$this->lowerAssocCase = (($conn->getFlags() & Creole::COMPAT_ASSOC_LOWER) === Creole::COMPAT_ASSOC_LOWER);
$this->rtrimString = (($conn->getFlags() & Creole::COMPAT_RTRIM_STRING) === Creole::COMPAT_RTRIM_STRING);
}
/**
* Destructor
*
* Free db result resource.
*/
public function __destruct()
{
$this->close();
}
/**
* @see ResultSet::getIterator()
*/
public function getIterator()
{
require_once 'creole/ResultSetIterator.php';
return new ResultSetIterator($this);
}
/**
* @see ResultSet::getResource()
*/
public function getResource()
{
return $this->result;
}
/**
* @see ResultSet::isLowereAssocCase()
*/
public function isLowerAssocCase()
{
return $this->lowerAssocCase;
}
/**
* @see ResultSet::setFetchmode()
*/
public function setFetchmode($mode)
{
$this->fetchmode = $mode;
}
/**
* @see ResultSet::getFetchmode()
*/
public function getFetchmode()
{
return $this->fetchmode;
}
/**
* @see ResultSet::previous()
*/
public function previous()
{
// Go back 2 spaces so that we can then advance 1 space.
$ok = $this->seek($this->cursorPos - 2);
if ($ok === false) {
$this->beforeFirst();
return false;
}
return $this->next();
}
/**
* @see ResultSet::isBeforeFirst()
*/
public function relative($offset)
{
// which absolute row number are we seeking
$pos = $this->cursorPos + ($offset - 1);
$ok = $this->seek($pos);
if ($ok === false) {
if ($pos < 0) {
$this->beforeFirst();
} else {
$this->afterLast();
}
} else {
$ok = $this->next();
}
return $ok;
}
/**
* @see ResultSet::absolute()
*/
public function absolute($pos)
{
$ok = $this->seek( $pos - 1 ); // compensate for next() factor
if ($ok === false) {
if ($pos - 1 < 0) {
$this->beforeFirst();
} else {
$this->afterLast();
}
} else {
$ok = $this->next();
}
return $ok;
}
/**
* @see ResultSet::first()
*/
public function first()
{
if($this->cursorPos !== 0) { $this->seek(0); }
return $this->next();
}
/**
* @see ResultSet::last()
*/
public function last()
{
if($this->cursorPos !== ($last = $this->getRecordCount() - 1)) {
$this->seek( $last );
}
return $this->next();
}
/**
* @see ResultSet::beforeFirst()
*/
public function beforeFirst()
{
$this->cursorPos = 0;
}
/**
* @see ResultSet::afterLast()
*/
public function afterLast()
{
$this->cursorPos = $this->getRecordCount() + 1;
}
/**
* @see ResultSet::isAfterLast()
*/
public function isAfterLast()
{
return ($this->cursorPos === $this->getRecordCount() + 1);
}
/**
* @see ResultSet::isBeforeFirst()
*/
public function isBeforeFirst()
{
return ($this->cursorPos === 0);
}
/**
* @see ResultSet::getCursorPos()
*/
public function getCursorPos()
{
return $this->cursorPos;
}
/**
* @see ResultSet::getRow()
*/
public function getRow()
{
return $this->fields;
}
/**
* @see ResultSet::get()
*/
public function get($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
return $this->fields[$idx];
}
/**
* @see ResultSet::getArray()
*/
public function getArray($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
return (array) unserialize($this->fields[$idx]);
}
/**
* @see ResultSet::getBoolean()
*/
public function getBoolean($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
return (boolean) $this->fields[$idx];
}
/**
* @see ResultSet::getBlob()
*/
public function getBlob($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
require_once 'creole/util/Blob.php';
$b = new Blob();
$b->setContents($this->fields[$idx]);
return $b;
}
/**
* @see ResultSet::getClob()
*/
public function getClob($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
require_once 'creole/util/Clob.php';
$c = new Clob();
$c->setContents($this->fields[$idx]);
return $c;
}
/**
* @see ResultSet::getDate()
*/
public function getDate($column, $format = '%x')
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
$ts = strtotime($this->fields[$idx]);
if ($ts === -1 || $ts === false) { // in PHP 5.1 return value changes to FALSE
throw new SQLException("Unable to convert value at column " . $column . " to timestamp: " . $this->fields[$idx]);
}
if ($format === null) {
return $ts;
}
if (strpos($format, '%') !== false) {
return strftime($format, $ts);
} else {
return date($format, $ts);
}
}
/**
* @see ResultSet::getFloat()
*/
public function getFloat($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
return (float) $this->fields[$idx];
}
/**
* @see ResultSet::getInt()
*/
public function getInt($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
return (int) $this->fields[$idx];
}
/**
* @see ResultSet::getString()
*/
public function getString($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
return ($this->rtrimString ? rtrim($this->fields[$idx]) : (string) $this->fields[$idx]);
}
/**
* @see ResultSet::getTime()
*/
public function getTime($column, $format = '%X')
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
$ts = strtotime($this->fields[$idx]);
if ($ts === -1 || $ts === false) { // in PHP 5.1 return value changes to FALSE
throw new SQLException("Unable to convert value at column " . (is_int($column) ? $column + 1 : $column) . " to timestamp: " . $this->fields[$idx]);
}
if ($format === null) {
return $ts;
}
if (strpos($format, '%') !== false) {
return strftime($format, $ts);
} else {
return date($format, $ts);
}
}
/**
* @see ResultSet::getTimestamp()
*/
public function getTimestamp($column, $format = 'Y-m-d H:i:s')
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
$ts = strtotime($this->fields[$idx]);
if ($ts === -1 || $ts === false) { // in PHP 5.1 return value changes to FALSE
throw new SQLException("Unable to convert value at column " . $column . " to timestamp: " . $this->fields[$idx]);
}
if ($format === null) {
return $ts;
}
if (strpos($format, '%') !== false) {
return strftime($format, $ts);
} else {
return date($format, $ts);
}
}
}

View File

@ -0,0 +1,289 @@
<?php
/*
* $Id: StatementCommon.php,v 1.4 2004/06/13 02:31:07 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Class that contains common/shared functionality for Statements.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.4 $
* @package creole.common
*/
abstract class StatementCommon {
/**
* The database connection.
* @var Connection
*/
protected $conn;
/**
* Temporarily hold a ResultSet object after an execute() query.
* @var ResultSet
*/
protected $resultSet;
/**
* Temporary hold the affected row cound after an execute() query.
* @var int
*/
protected $updateCount;
/**
* Array of warning objects generated by methods performed on result set.
* @var array SQLWarning[]
*/
protected $warnings = array();
/**
* The ResultSet class name.
* @var string
*/
protected $resultClass;
/**
* The prepared statement resource id.
* @var resource
*/
protected $stmt;
/**
* Max rows to retrieve from DB.
* @var int
*/
protected $limit = 0;
/**
* Offset at which to start processing DB rows.
* "Skip X rows"
* @var int
*/
protected $offset = 0;
/**
* Create new statement instance.
*
* @param Connection $conn Connection object
*/
function __construct(Connection $conn)
{
$this->conn = $conn;
}
/**
* Sets the maximum number of rows to return from db.
* This will affect the SQL if the RDBMS supports native LIMIT; if not,
* it will be emulated. Limit only applies to queries (not update sql).
* @param int $v Maximum number of rows or 0 for all rows.
* @return void
*/
public function setLimit($v)
{
$this->limit = (int) $v;
}
/**
* Returns the maximum number of rows to return or 0 for all.
* @return int
*/
public function getLimit()
{
return $this->limit;
}
/**
* Sets the start row.
* This will affect the SQL if the RDBMS supports native OFFSET; if not,
* it will be emulated. Offset only applies to queries (not update) and
* only is evaluated when LIMIT is set!
* @param int $v
* @return void
*/
public function setOffset($v)
{
$this->offset = (int) $v;
}
/**
* Returns the start row.
* Offset only applies when Limit is set!
* @return int
*/
public function getOffset()
{
return $this->offset;
}
/**
* Free resources associated with this statement.
* Some drivers will need to implement this method to free
* database result resources.
*
* @return void
*/
public function close()
{
// do nothing here (subclasses will implement)
}
/**
* Generic execute() function has to check to see whether SQL is an update or select query.
*
* If you already know whether it's a SELECT or an update (manipulating) SQL, then use
* the appropriate method, as this one will incurr overhead to check the SQL.
*
* @param int $fetchmode Fetchmode (only applies to queries).
* @return boolean True if it is a result set, false if not or if no more results (this is identical to JDBC return val).
* @throws SQLException
* @todo -cStatementCommon Update execute() to not use isSelect() method, but rather to determine type based on returned results.
*/
public function execute($sql, $fetchmode = null)
{
if (!$this->isSelect($sql)) {
$this->updateCount = $this->executeUpdate($sql);
return false;
} else {
$this->resultSet = $this->executeQuery($sql, $fetchmode);
if ($this->resultSet->getRecordCount() === 0) {
return false;
}
return true;
}
}
/**
* Get result set.
* This assumes that the last thing done was an executeQuery() or an execute()
* with SELECT-type query.
*
* @return RestultSet (or null if none)
*/
public function getResultSet()
{
return $this->resultSet;
}
/**
* Get update count.
*
* @return int Number of records affected, or <code>null</code> if not applicable.
*/
public function getUpdateCount()
{
return $this->updateCount;
}
/**
* Returns whether the passed SQL is a SELECT statement.
*
* Returns true if SQL starts with 'SELECT' but not 'SELECT INTO'. This exists
* to support the execute() function -- which could either execute an update or
* a query.
*
* Currently this function does not take into consideration comments, primarily
* because there are a number of different comment options for different drivers:
* <pre>
* -- SQL-defined comment, but not truly comment in Oracle
* # comment in mysql
* /* comment in mssql, others * /
* // comment sometimes?
* REM also comment ...
* </pre>
*
* If you're wondering why we can't just execute the query and look at the return results
* to see whether it was an update or a select, the reason is that for update queries we
* need to do stuff before we execute them -- like start transactions if auto-commit is off.
*
* @param string $sql
* @return boolean Whether statement is a SELECT SQL statement.
* @see execute()
*/
protected function isSelect($sql)
{
// is first word is SELECT, then return true, unless it's SELECT INTO ...
// this doesn't, however, take comments into account ...
$sql = trim($sql);
return (stripos($sql, 'select') === 0 && stripos($sql, 'select into ') !== 0);
}
/**
* Executes the SQL query in this PreparedStatement object and returns the resultset generated by the query.
*
* @param string $sql This method may optionally be called with the SQL statement.
* @param int $fetchmode The mode to use when fetching the results (e.g. ResultSet::FETCHMODE_NUM, ResultSet::FETCHMODE_ASSOC).
* @return object Creole::ResultSet
* @throws SQLException If there is an error executing the specified query.
* @todo -cStatementCommon Put native query execution logic in statement subclasses.
*/
public function executeQuery($sql, $fetchmode = null)
{
$this->updateCount = null;
if ($this->limit > 0 || $this->offset > 0) {
$this->conn->applyLimit($sql, $this->offset, $this->limit);
}
$this->resultSet = $this->conn->executeQuery($sql, $fetchmode);
return $this->resultSet;
}
/**
* Executes the SQL INSERT, UPDATE, or DELETE statement in this PreparedStatement object.
*
* @param string $sql This method may optionally be called with the SQL statement.
* @return int Number of affected rows (or 0 for drivers that return nothing).
* @throws SQLException if a database access error occurs.
*/
public function executeUpdate($sql)
{
if ($this->resultSet) $this->resultSet->close();
$this->resultSet = null;
$this->updateCount = $this->conn->executeUpdate($sql);
return $this->updateCount;
}
/**
* Gets next result set (if this behavior is supported by driver).
* Some drivers (e.g. MSSQL) support returning multiple result sets -- e.g.
* from stored procedures.
*
* This function also closes any current restult set.
*
* Default behavior is for this function to return false. Driver-specific
* implementations of this class can override this method if they actually
* support multiple result sets.
*
* @return boolean True if there is another result set, otherwise false.
*/
public function getMoreResults()
{
if ($this->resultSet) $this->resultSet->close();
$this->resultSet = null;
return false;
}
/**
* Gets the db Connection that created this statement.
* @return Connection
*/
public function getConnection()
{
return $this->conn;
}
}

View File

@ -0,0 +1,478 @@
<?php
/*
* $Id: MSSQLCallableStatement.php,v 1.20 2005/09/16 13:09:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/drivers/mssql/MSSQLPreparedStatement.php';
require_once 'creole/CallableStatement.php';
include_once 'creole/CreoleTypes.php';
/**
* MS SQL Server class to handle stored procedure execution.
*
* Developer note:
*
* There is no CallableStatement superclass. Unlike JDBC, Creole
* uses abstract parent classes rather than interfaces -- in order
* to minimize code duplication. Since PHP doesn't support multiple
* inheritance, the DRIVERCallableStatement class cannot extend both
* the DRIVERPreparedStatement class and the would-be abstract
* CallableStatement class.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.20 $
* @package creole.drivers.mssql
*/
class MSSQLCallableStatement extends MSSQLPreparedStatement implements CallableStatement {
/** Output variables */
private $boundOutVars = array();
/**
* Match Creole types to SQL Server types
* @var array
*/
private static $typeMap = array(
CreoleTypes::BOOLEAN => SQLBIT,
CreoleTypes::BIGINT => SQLINT4,
CreoleTypes::SMALLINT => SQLINT2,
CreoleTypes::TINYINT => SQLINT2,
CreoleTypes::INTEGER => SQLINT4,
CreoleTypes::CHAR => SQLCHAR,
CreoleTypes::VARCHAR => SQLVARCHAR,
CreoleTypes::TEXT => SQLTEXT,
CreoleTypes::FLOAT => SQLFLT8,
CreoleTypes::DOUBLE => SQLFLT8,
CreoleTypes::DATE => SQLVARCHAR,
CreoleTypes::TIME => SQLVARCHAR,
CreoleTypes::TIMESTAMP => SQLVARCHAR,
CreoleTypes::VARBINARY => SQLVARCHAR,
CreoleTypes::NUMERIC => SQLINT4,
CreoleTypes::DECIMAL => SQLFLT8
);
/**
* Statement created by mssql_init()
* @var resource
*/
private $stmt;
/**
* The result resource.
* @var resource
*/
private $result;
/**
* Construct new MSSQLCallableStatement.
*
* @param Connection $conn
* @param resource $stmt
*/
public function __construct(Connection $conn, $stmt)
{
print " - > IN CONSTRUCTOR \n";
$this->conn = $conn;
$this->stmt = $stmt;
}
/**
* @see CallableStatement::getResource()
*/
public function getResource()
{
return $this->stmt;
}
/**
* @see CallableStatement::close()
*/
function close()
{
@mssql_free_statement($this->stmt);
$this->rsFetchCount = 0;
}
/**
* @see CallableStatement::executeQuery()
*/
function executeQuery($p1 = null, $fetchmode = null)
{
$params = null;
if ($fetchmode !== null) {
$params = $p1;
} elseif ($p1 !== null) {
if (is_array($p1)) $params = $p1;
else $fetchmode = $p1;
}
if ($params) {
for($i=0,$cnt=count($params); $i < $cnt; $i++) {
$this->set($i+1, $params[$i]);
}
}
$this->result = mssql_execute($this->stmt);
if (!$this->result) {
throw new SQLException('unable to execute callable statement', mssql_get_last_message());
}
return new MSSQLResultSet($this->conn, $this->result, $fetchmode, $this->offset, $this->limit);
}
/**
* @see CallableStatement::getMoreResults()
*/
function getMoreResults()
{
$this->rsFetchCount++; // we track this because
$hasMore = mssql_next_result($this->result);
if ($this->resultSet) $this->resultSet->close();
if ($hasMore) {
$clazz = $this->resultClass;
$this->resultSet = new $clazz($this, $this->result);
} else {
$this->resultSet = null;
}
return $hasMore;
}
/**
* @see CallableStatement::registerOutParameter()
*/
function registerOutParameter($paramIndex, $sqlType, $maxLength = null)
{
mssql_bind($this->stmt, $paramIndex, $this->boundOutVars[$paramIndex], self::$typeMap[$sqlType], true, false, $maxLength);
}
/**
* @see CallableStatement::setArray()
*/
function setArray($paramIndex, $value, $out = false)
{
if ($out) $this->boundOutVars[$paramIndex] = &$value; // reference means that changes to value, will be reflected
if ($value === null) {
$this->setNull($paramIndex);
} else {
$value = serialize($value);
mssql_bind($this->stmt, $paramIndex, $value, SQLTEXT, $out);
}
}
/**
* @see CallableStatement::setBoolean()
*/
function setBoolean($paramIndex, $value, $out = false)
{
if ($out) $this->boundOutVars[$paramIndex] = &$value; // reference means that changes to value, will be reflected
if ($value === null) {
$this->setNull($paramIndex);
} else {
$value = ($value) ? 1 : 0;
mssql_bind($this->stmt, $paramIndex, $value, SQLBIT, $out);
}
}
/**
* @see CallableStatement::setBlob()
*/
function setBlob($paramIndex, $blob, $out = false)
{
if ($blob === null) {
$this->setNull($paramIndex);
} else {
if (is_object($blob)) {
$blob = $blob->__toString();
}
if ($out) $this->boundOutVars[$paramIndex] = &$blob; // reference means that changes to value, will be reflected
$data = unpack("H*hex", $blob);
mssql_bind($this->stmt, $paramIndex, $data, SQLTEXT, $out);
}
}
/**
* @see CallableStatement::setClob()
*/
function setClob($paramIndex, $clob, $out = false)
{
if ($clob === null) {
$this->setNull($paramIndex);
} else {
if (is_object($clob)) {
$clob = $clob->__toString();
}
if ($out) $this->boundOutVars[$paramIndex] = &$clob; // reference means that changes to value, will be reflected
mssql_bind($this->stmt, $paramIndex, $clob, SQLTEXT, $out);
}
}
/**
* @see CallableStatement::setDate()
*/
function setDate($paramIndex, $value, $out = false)
{
if ($out) $this->boundOutVars[$paramIndex] = &$value; // reference means that changes to value, will be reflected
if ($value === null) {
$this->setNull($paramIndex);
} else {
if (is_numeric($value)) $value = date("Y-m-d", $value);
mssql_bind($this->stmt, $paramIndex, $value, SQLVARCHAR, $out);
}
}
/**
* @see CallableStatement::setFloat()
*/
function setFloat($paramIndex, $value, $out = false)
{
if ($out) $this->boundOutVars[$paramIndex] = &$value; // reference means that changes to value, will be reflected
if ($value === null) {
$this->setNull($paramIndex);
} else {
$value = (float) $value;
mssql_bind($this->stmt, $paramIndex, $value, SQLFLT8, $out);
}
}
/**
* @see CallableStatement::setInt()
*/
function setInt($paramIndex, $value, $out = false)
{
if ($out) $this->boundOutVars[$paramIndex] = &$value; // reference means that changes to value, will be reflected
if ($value === null) {
$this->setNull($paramIndex);
} else {
$value = (int) $value;
mssql_bind($this->stmt, $paramIndex, $value, SQLINT4, $out);
}
}
/**
* @see CallableStatement::setNull()
*/
function setNull($paramIndex)
{
// hopefully type isn't essential here :)
$value = null; // wants a var to pass by reference
mssql_bind($this->stmt, $paramIndex, $value, $type=null, $out=false, $is_null=true);
}
/**
* @see CallableStatement::setString()
*/
function setString($paramIndex, $value, $out = false)
{
if ($out) $this->boundOutVars[$paramIndex] = &$value; // reference means that changes to value, will be reflected
if ($value === null) {
$this->setNull($paramIndex);
} else {
$value = (string) $value;
mssql_bind($this->stmt, $paramIndex, $value, SQLVARCHAR, $out);
}
}
/**
* @see CallableStatement::setTime()
*/
function setTime($paramIndex, $value, $out = false)
{
if ($out) $this->boundOutVars[$paramIndex] = &$value; // reference means that changes to value, will be reflected
if ($value === null) {
$this->setNull($paramIndex);
} else {
if (is_numeric($value)) $value = date("H:i:s", $value);
mssql_bind($this->stmt, $paramIndex, $value, SQLVARCHAR, $out);
}
}
/**
* @see CallableStatement::setTimestamp()
*/
function setTimestamp($paramIndex, $value, $out = false)
{
if ($out) $this->boundOutVars[$paramIndex] = &$value; // reference means that changes to value, will be reflected
if ($value === null) {
$this->setNull($paramIndex);
} else {
if (is_numeric($value)) $value = date('Y-m-d H:i:s', $value);
mssql_bind($this->stmt, $paramIndex, $value, SQLVARCHAR, $out);
}
}
/**
* @see CallableStatement::getArray()
*/
function getArray($paramIndex)
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
return (array) unserialize($this->boundOutVars[$paramIndex]);
}
/**
* @see CallableStatement::getBoolean()
*/
function getBoolean($paramIndex)
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
return (boolean) $this->boundOutVars[$paramIndex];
}
/**
* @see CallableStatement::getBlob()
*/
function getBlob($paramIndex)
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
require_once 'creole/util/Blob.php';
$b = new Blob();
$b->setContents($this->boundOutVars[$paramIndex]);
return $b;
}
/**
* @see CallableStatement::getClob()
*/
function getClob($paramIndex)
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
require_once 'creole/util/Clob.php';
$c = new Clob();
$c->setContents($this->boundOutVars[$paramIndex]);
return $c;
}
/**
* @see CallableStatement::getDate()
*/
function getDate($paramIndex, $fmt = '%Y-%m-%d')
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
$ts = strtotime($this->boundOutVars[$paramIndex]);
if ($ts === -1 || $ts === false) { // in PHP 5.1 return value changes to FALSE
throw new SQLException("Unable to convert value at column " . $paramIndex . " to timestamp: " . $this->boundOutVars[$paramIndex]);
}
if (strpos($format, '%') !== false) {
return strftime($format, $ts);
} else {
return date($format, $ts);
}
return $this->boundOutVars[$paramIndex];
}
/**
* @param mixed $paramIndex Column name (string) or index (int).
* @return float
*/
function getFloat($paramIndex)
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
return (float) $this->boundOutVars[$paramIndex];
}
/**
* @see CallableStatement::getInt()
*/
function getInt($paramIndex)
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
return (int) $this->boundOutVars[$paramIndex];
}
/**
* @see CallableStatement::getString()
*/
function getString($paramIndex)
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
return (string) $this->boundOutVars[$paramIndex];
}
/**
* @see CallableStatement::getTime()
*/
function getTime($paramIndex, $format='%X')
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
$ts = strtotime($this->boundOutVars[$paramIndex]);
if ($ts === -1 || $ts === false) { // in PHP 5.1 return value changes to FALSE
throw new SQLException("Unable to convert value at column " . $paramIndex . " to timestamp: " . $this->boundOutVars[$paramIndex]);
}
if (strpos($format, '%') !== false) {
return strftime($format, $ts);
} else {
return date($format, $ts);
}
}
/**
* @see CallableStatement::getTimestamp()
*/
function getTimestamp($paramIndex, $format = 'Y-m-d H:i:s')
{
if (!array_key_exists($paramIndex, $this->boundOutVars)) {
throw new SQLException('Requesting variable not bound to output var: '.$paramIndex);
}
if ($this->boundOutVars[$paramIndex] === null) { return null; }
$ts = strtotime($this->boundOutVars[$paramIndex]);
if ($ts === -1 || $ts === false) { // in PHP 5.1 return value changes to FALSE
throw new SQLException("Unable to convert value at column " . $paramIndex . " to timestamp: " . $this->boundOutVars[$paramIndex]);
}
if (strpos($format, '%') !== false) {
return strftime($format, $ts);
} else {
return date($format, $ts);
}
}
}

View File

@ -0,0 +1,283 @@
<?php
/*
* $Id: MSSQLConnection.php,v 1.25 2005/10/17 19:03:51 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Connection.php';
require_once 'creole/common/ConnectionCommon.php';
include_once 'creole/drivers/mssql/MSSQLResultSet.php';
/**
* MS SQL Server implementation of Connection.
*
* If you have trouble with BLOB / CLOB support
* --------------------------------------------
*
* You may need to change some PHP ini settings. In particular, the following settings
* set the text size to maximum which should get around issues with truncated data:
* <code>
* ini_set('mssql.textsize', 2147483647);
* ini_set('mssql.textlimit', 2147483647);
* </code>
* We do not set these by default (anymore) because they do not apply to cases where MSSQL
* is being used w/ FreeTDS.
*
* @author Hans Lellelid <hans@xmpl.org>
* @author Stig Bakken <ssb@fast.no>
* @author Lukas Smith
* @version $Revision: 1.25 $
* @package creole.drivers.mssql
*/
class MSSQLConnection extends ConnectionCommon implements Connection {
/** Current database (used in mssql_select_db()). */
private $database;
/**
* @see Connection::connect()
*/
function connect($dsninfo, $flags = 0)
{
if (!extension_loaded('mssql') && !extension_loaded('sybase') && !extension_loaded('sybase_ct')) {
throw new SQLException('mssql extension not loaded');
}
$this->dsn = $dsninfo;
$this->flags = $flags;
$persistent = ($flags & Creole::PERSISTENT === Creole::PERSISTENT);
$user = $dsninfo['username'];
$pw = $dsninfo['password'];
$dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
if (PHP_OS == "WINNT" || PHP_OS == "WIN32") {
$portDelimiter = ",";
} else {
$portDelimiter = ":";
}
if(!empty($dsninfo['port'])) {
$dbhost .= $portDelimiter.$dsninfo['port'];
} else {
$dbhost .= $portDelimiter.'1433';
}
$connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect';
if ($dbhost && $user && $pw) {
$conn = @$connect_function($dbhost, $user, $pw);
} elseif ($dbhost && $user) {
$conn = @$connect_function($dbhost, $user);
} else {
$conn = @$connect_function($dbhost);
}
if (!$conn) {
throw new SQLException('connect failed', mssql_get_last_message());
}
if ($dsninfo['database']) {
if (!@mssql_select_db($dsninfo['database'], $conn)) {
throw new SQLException('No database selected');
}
$this->database = $dsninfo['database'];
}
$this->dblink = $conn;
}
/**
* @see Connection::getDatabaseInfo()
*/
public function getDatabaseInfo()
{
require_once 'creole/drivers/mssql/metadata/MSSQLDatabaseInfo.php';
return new MSSQLDatabaseInfo($this);
}
/**
* @see Connection::getIdGenerator()
*/
public function getIdGenerator()
{
require_once 'creole/drivers/mssql/MSSQLIdGenerator.php';
return new MSSQLIdGenerator($this);
}
/**
* @see Connection::prepareStatement()
*/
public function prepareStatement($sql)
{
require_once 'creole/drivers/mssql/MSSQLPreparedStatement.php';
return new MSSQLPreparedStatement($this, $sql);
}
/**
* @see Connection::createStatement()
*/
public function createStatement()
{
require_once 'creole/drivers/mssql/MSSQLStatement.php';
return new MSSQLStatement($this);
}
/**
* Returns false since MSSQL doesn't support this method.
*/
public function applyLimit(&$sql, $offset, $limit)
{
return false;
}
/**
* @see Connection::close()
*/
function close()
{
$ret = @mssql_close($this->dblink);
$this->dblink = null;
return $ret;
}
/**
* @see Connection::executeQuery()
*/
function executeQuery($sql, $fetchmode = null)
{
$this->lastQuery = $sql;
if (!@mssql_select_db($this->database, $this->dblink)) {
throw new SQLException('No database selected');
}
$result = @mssql_query($sql, $this->dblink);
if (!$result) {
throw new SQLException('Could not execute query', mssql_get_last_message());
}
return new MSSQLResultSet($this, $result, $fetchmode);
}
/**
* @see Connection::executeUpdate()
*/
function executeUpdate($sql)
{
$this->lastQuery = $sql;
if (!mssql_select_db($this->database, $this->dblink)) {
throw new SQLException('No database selected');
}
$result = @mssql_query($sql, $this->dblink);
if (!$result) {
throw new SQLException('Could not execute update', mssql_get_last_message(), $sql);
}
return $this->getUpdateCount();
}
/**
* Start a database transaction.
* @throws SQLException
* @return void
*/
protected function beginTrans()
{
$result = @mssql_query('BEGIN TRAN', $this->dblink);
if (!$result) {
throw new SQLException('Could not begin transaction', mssql_get_last_message());
}
}
/**
* Commit the current transaction.
* @throws SQLException
* @return void
*/
protected function commitTrans()
{
if (!@mssql_select_db($this->database, $this->dblink)) {
throw new SQLException('No database selected');
}
$result = @mssql_query('COMMIT TRAN', $this->dblink);
if (!$result) {
throw new SQLException('Could not commit transaction', mssql_get_last_message());
}
}
/**
* Roll back (undo) the current transaction.
* @throws SQLException
* @return void
*/
protected function rollbackTrans()
{
if (!@mssql_select_db($this->database, $this->dblink)) {
throw new SQLException('no database selected');
}
$result = @mssql_query('ROLLBACK TRAN', $this->dblink);
if (!$result) {
throw new SQLException('Could not rollback transaction', mssql_get_last_message());
}
}
/**
* 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
* @throws SQLException
*/
function getUpdateCount()
{
$res = @mssql_query('select @@rowcount', $this->dblink);
if (!$res) {
throw new SQLException('Unable to get affected row count', mssql_get_last_message());
}
$ar = @mssql_fetch_row($res);
if (!$ar) {
$result = 0;
} else {
@mssql_free_result($res);
$result = $ar[0];
}
return $result;
}
/**
* Creates a CallableStatement object for calling database stored procedures.
*
* @param string $sql
* @return CallableStatement
* @throws SQLException
*/
function prepareCall($sql)
{
require_once 'creole/drivers/mssql/MSSQLCallableStatement.php';
$stmt = mssql_init($sql);
if (!$stmt) {
throw new SQLException('Unable to prepare statement', mssql_get_last_message(), $sql);
}
return new MSSQLCallableStatement($this, $stmt);
}
}

View File

@ -0,0 +1,62 @@
<?php
require_once 'creole/IdGenerator.php';
/**
* MSSQL IdGenerator implimenation.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.6 $
* @package creole.drivers.mssql
*/
class MSSQLIdGenerator implements IdGenerator {
/** Connection object that instantiated this class */
private $conn;
/**
* Creates a new IdGenerator class, saves passed connection for use
* later by getId() method.
* @param Connection $conn
*/
public function __construct(Connection $conn)
{
$this->conn = $conn;
}
/**
* @see IdGenerator::isBeforeInsert()
*/
public function isBeforeInsert()
{
return false;
}
/**
* @see IdGenerator::isAfterInsert()
*/
public function isAfterInsert()
{
return true;
}
/**
* @see IdGenerator::getIdMethod()
*/
public function getIdMethod()
{
return self::AUTOINCREMENT;
}
/**
* @see IdGenerator::getId()
*/
public function getId($unused = null)
{
$rs = $this->conn->executeQuery("SELECT SCOPE_IDENTITY()", ResultSet::FETCHMODE_NUM);
$rs->next();
return $rs->getInt(1);
}
}

View File

@ -0,0 +1,99 @@
<?php
/*
* $Id: MSSQLPreparedStatement.php,v 1.13 2005/11/13 01:29:01 gamr Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/PreparedStatement.php';
require_once 'creole/common/PreparedStatementCommon.php';
/**
* MSSQL specific PreparedStatement functions.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.13 $
* @package creole.drivers.mssql
*/
class MSSQLPreparedStatement extends PreparedStatementCommon implements PreparedStatement {
/**
* MSSQL-specific implementation of setBlob().
*
* If you are having trouble getting BLOB data into the database, see the phpdoc comment
* in the MSSQLConnection for some PHP ini values that may need to be set. (This also
* applies to CLOB support.)
*
* @param int $paramIndex
* @param mixed $value Blob object or string.
* @return void
*/
function setBlob($paramIndex, $blob)
{
$this->sql_cache_valid = false;
if ($blob === null) {
$this->setNull($paramIndex);
} else {
// they took magic __toString() out of PHP5.0.0; this sucks
if (is_object($blob)) {
$blob = $blob->__toString();
}
$data = unpack("H*hex", $blob);
$this->boundInVars[$paramIndex] = '0x'.$data['hex']; // no surrounding quotes!
}
}
/**
* Add quotes using str_replace.
* This is not as thorough as MySQL.
*/
protected function escape($subject)
{
// use this instead of magic_quotes_sybase + addslashes(),
// just in case multiple RDBMS being used at the same time
return str_replace("'", "''", $subject);
}
/**
* MSSQL must emulate OFFSET/LIMIT support.
*/
public function executeQuery($p1 = null, $fetchmode = null)
{
$params = null;
if ($fetchmode !== null) {
$params = $p1;
} elseif ($p1 !== null) {
if (is_array($p1)) $params = $p1;
else $fetchmode = $p1;
}
if ($params) {
for($i=0,$cnt=count($params); $i < $cnt; $i++) {
$this->set($i+1, $params[$i]);
}
}
$this->updateCount = null; // reset
$sql = $this->replaceParams();
$this->resultSet = $this->conn->executeQuery($sql, $fetchmode);
$this->resultSet->_setOffset($this->offset);
$this->resultSet->_setLimit($this->limit);
return $this->resultSet;
}
}

View File

@ -0,0 +1,159 @@
<?php
/*
* $Id: MSSQLResultSet.php,v 1.21 2006/01/17 19:44:38 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/ResultSet.php';
require_once 'creole/common/ResultSetCommon.php';
/**
* MSSQL implementation of ResultSet.
*
* MS SQL does not support LIMIT or OFFSET natively so the methods
* in here need to perform some adjustments and extra checking to make sure
* that this behaves the same as RDBMS drivers using native OFFSET/LIMIT.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.21 $
* @package creole.drivers.mssql
*/
class MSSQLResultSet extends ResultSetCommon implements ResultSet {
/**
* Offset at which to start reading rows.
* @var int
*/
private $offset = 0;
/**
* Maximum rows to retrieve, or 0 if all.
* @var int
*/
private $limit = 0;
/**
* This MSSQL-only function exists to set offset after ResultSet is instantiated.
* This function should be "protected" in Java sense: only available to classes in package.
* THIS METHOD SHOULD NOT BE CALLED BY ANYTHING EXCEPTION DRIVER CLASSES.
* @param int $offset New offset. If great than 0, then seek(0) will be called to move cursor.
* @access protected
*/
public function _setOffset($offset)
{
$this->offset = $offset;
if ($offset > 0) {
$this->seek(0); // 0 becomes $offset by seek() method
}
}
/**
* This MSSQL-only function exists to set limit after ResultSet is instantiated.
* This function should be "protected" in Java sense: only available to classes in package.
* THIS METHOD SHOULD NOT BE CALLED BY ANYTHING EXCEPTION DRIVER CLASSES.
* @param int $limit New limit.
* @access protected
*/
public function _setLimit($limit)
{
$this->limit = $limit;
}
/**
* @see ResultSet::seek()
*/
function seek($rownum)
{
// support emulated OFFSET
$actual = $rownum + $this->offset;
if (($this->limit > 0 && $rownum >= $this->limit) || $rownum < 0) {
// have to check for rownum < 0, because mssql_seek() won't
// complain if the $actual is valid.
return false;
}
// MSSQL rows start w/ 0, but this works, because we are
// looking to move the position _before_ the next desired position
if (!@mssql_data_seek($this->result, $actual)) {
return false;
}
$this->cursorPos = $rownum;
return true;
}
/**
* @see ResultSet::next()
*/
function next()
{
// support emulated LIMIT
if ( $this->limit > 0 && ($this->cursorPos >= $this->limit) ) {
$this->afterLast();
return false;
}
$this->fields = mssql_fetch_array($this->result, $this->fetchmode);
if (!$this->fields) {
if ($errmsg = mssql_get_last_message()) {
throw new SQLException("Error fetching result", $errmsg);
} else {
// We've advanced beyond end of recordset.
$this->afterLast();
return false;
}
}
if ($this->fetchmode === ResultSet::FETCHMODE_ASSOC && $this->lowerAssocCase) {
$this->fields = array_change_key_case($this->fields, CASE_LOWER);
}
// Advance cursor position
$this->cursorPos++;
return true;
}
/**
* @see ResultSet::getRecordCount()
*/
function getRecordCount()
{
$rows = @mssql_num_rows($this->result);
if ($rows === null) {
throw new SQLException('Error getting record count', mssql_get_last_message());
}
// adjust count based on emulated LIMIT/OFFSET
$rows -= $this->offset;
return ($this->limit > 0 && $rows > $this->limit ? $this->limit : $rows);
}
/**
* @see ResultSet::close()
*/
function close()
{
$ret = @mssql_free_result($this->result);
$this->result = false;
$this->fields = array();
$this->limit = 0;
$this->offset = 0;
}
}

View File

@ -0,0 +1,72 @@
<?php
/*
* $Id: MSSQLStatement.php,v 1.4 2004/06/13 02:31:07 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/common/StatementCommon.php';
require_once 'creole/Statement.php';
/**
* Class that contains MSSQL functionality for Statements.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.4 $
* @package creole.drivers.mssql
*/
class MSSQLStatement extends StatementCommon implements Statement {
/**
* Executes the SQL query in this PreparedStatement object and returns the resultset generated by the query.
*
* @param string $sql This method may optionally be called with the SQL statement.
* @param int $fetchmode The mode to use when fetching the results (e.g. ResultSet::FETCHMODE_NUM, ResultSet::FETCHMODE_ASSOC).
* @return object Creole::ResultSet
* @throws SQLException If there is an error executing the specified query.
*/
public function executeQuery($sql, $fetchmode = null)
{
$this->updateCount = null;
$this->resultSet = $this->conn->executeQuery($sql, $fetchmode);
$this->resultSet->_setOffset($this->offset);
$this->resultSet->_setLimit($this->limit);
return $this->resultSet;
}
/**
* Gets next result set (if this behavior is supported by driver).
* Some drivers (e.g. MSSQL) support returning multiple result sets -- e.g.
* from stored procedures.
*
* This function also closes any current restult set.
*
* Default behavior is for this function to return false. Driver-specific
* implementations of this class can override this method if they actually
* support multiple result sets.
*
* @return boolean True if there is another result set, otherwise false.
*/
public function getMoreResults()
{
if ($this->resultSet) $this->resultSet->close();
$this->resultSet = null;
return false;
}
}

View File

@ -0,0 +1,94 @@
<?php
/*
* $Id: MSSQLTypes.php,v 1.8 2004/07/27 23:16:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/CreoleTypes.php';
/**
* MSSQL types / type map.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.8 $
* @package creole.drivers.mssql
*/
class MSSQLTypes extends CreoleTypes {
/** Map PostgreSQL native types to Creole (JDBC) types. */
private static $typeMap = array (
"binary" => CreoleTypes::BINARY,
"bit" => CreoleTypes::BOOLEAN,
"char" => CreoleTypes::CHAR,
"datetime" => CreoleTypes::TIMESTAMP,
"decimal() identity" => CreoleTypes::DECIMAL,
"decimal" => CreoleTypes::DECIMAL,
"image" => CreoleTypes::LONGVARBINARY,
"int" => CreoleTypes::INTEGER,
"int identity" => CreoleTypes::INTEGER,
"integer" => CreoleTypes::INTEGER,
"money" => CreoleTypes::DECIMAL,
"nchar" => CreoleTypes::CHAR,
"ntext" => CreoleTypes::LONGVARCHAR,
"numeric() identity" => CreoleTypes::NUMERIC,
"numeric" => CreoleTypes::NUMERIC,
"nvarchar" => CreoleTypes::VARCHAR,
"real" => CreoleTypes::REAL,
"float" => CreoleTypes::FLOAT,
"smalldatetime" => CreoleTypes::TIMESTAMP,
"smallint" => CreoleTypes::SMALLINT,
"smallint identity" => CreoleTypes::SMALLINT,
"smallmoney" => CreoleTypes::DECIMAL,
"sysname" => CreoleTypes::VARCHAR,
"text" => CreoleTypes::LONGVARCHAR,
"timestamp" => CreoleTypes::BINARY,
"tinyint identity" => CreoleTypes::TINYINT,
"tinyint" => CreoleTypes::TINYINT,
"uniqueidentifier" => CreoleTypes::CHAR,
"varbinary" => CreoleTypes::VARBINARY,
"varchar" => CreoleTypes::VARCHAR,
"uniqueidentifier" => CreoleTypes::CHAR,
// SQL Server 2000 only
"bigint identity" => CreoleTypes::BIGINT,
"bigint" => CreoleTypes::BIGINT,
"sql_variant" => CreoleTypes::VARCHAR,
);
/** Reverse lookup map, created on demand. */
private static $reverseMap = null;
public static function getType($mssqlType)
{
$t = strtolower($mssqlType);
if (isset(self::$typeMap[$t])) {
return self::$typeMap[$t];
} else {
return CreoleTypes::OTHER;
}
}
public static function getNativeType($creoleType)
{
if (self::$reverseMap === null) {
self::$reverseMap = array_flip(self::$typeMap);
}
return @self::$reverseMap[$creoleType];
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* $Id: MSSQLDatabaseInfo.php,v 1.11 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/DatabaseInfo.php';
/**
* MSSQL impementation of DatabaseInfo.
*
* @author Hans Lellelid
* @version $Revision: 1.11 $
* @package creole.drivers.mssql.metadata
*/
class MSSQLDatabaseInfo extends DatabaseInfo {
/**
* @throws SQLException
* @return void
*/
protected function initTables()
{
include_once 'creole/drivers/mssql/metadata/MSSQLTableInfo.php';
$dsn = $this->conn->getDSN();
if (!@mssql_select_db($this->dbname, $this->conn->getResource())) {
throw new SQLException('No database selected');
}
$result = mssql_query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME <> 'dtproperties'", $this->conn->getResource());
if (!$result) {
throw new SQLException("Could not list tables", mssql_get_last_message());
}
while ($row = mssql_fetch_row($result)) {
$this->tables[strtoupper($row[0])] = new MSSQLTableInfo($this, $row[0]);
}
}
/**
*
* @return void
* @throws SQLException
*/
protected function initSequences()
{
// there are no sequences -- afaik -- in MSSQL.
}
}

View File

@ -0,0 +1,183 @@
<?php
/*
* $Id: MSSQLTableInfo.php,v 1.14 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/CreoleTypes.php';
require_once 'creole/metadata/TableInfo.php';
/**
* MSSQL implementation of TableInfo.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.14 $
* @package creole.drivers.mssql.metadata
*/
class MSSQLTableInfo extends TableInfo {
/**
* Loads the columns for this table.
* @return void
*/
protected function initColumns()
{
include_once 'creole/metadata/ColumnInfo.php';
include_once 'creole/drivers/mssql/MSSQLTypes.php';
if (!@mssql_select_db($this->dbname, $this->conn->getResource())) {
throw new SQLException('No database selected');
}
$res = mssql_query("sp_columns ".$this->name, $this->conn->getResource());
if (!$res) {
throw new SQLException('Could not get column names', mssql_get_last_message());
}
while ($row = mssql_fetch_array($res)) {
$name = $row['COLUMN_NAME'];
$type = $row['TYPE_NAME'];
$length = $row['LENGTH'];
$is_nullable = $row['NULLABLE'];
$default = $row['COLUMN_DEF'];
$precision = $row['PRECISION'];
$scale = $row['SCALE'];
$identity = false;
if (strtolower($type) == "int identity") {
$identity = true;
}
$this->columns[$name] = new ColumnInfo($this, $name, MSSQLTypes::getType($type), $type, $length, $precision, $scale, $is_nullable, $default, $identity);
}
$this->colsLoaded = true;
}
/**
* Loads the indexes for this table.
* @return void
*/
protected function initIndexes()
{
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
include_once 'creole/metadata/IndexInfo.php';
if (!@mssql_select_db($this->dbname, $this->conn->getResource())) {
throw new SQLException('No database selected');
}
$res = mssql_query("sp_indexes_rowset ".$this->name, $this->conn->getResource());
while ($row = mssql_fetch_array($res)) {
$name = $row['INDEX_NAME'];
// All primary keys are indexes (right...?)
if (!isset($this->indexes[$name])) {
$this->indexes[$name] = new IndexInfo($name);
}
$this->indexes[$name]->addColumn($this->columns[ $row['COLUMN_NAME'] ]);
}
$this->indexesLoaded = true;
}
/**
* Loads the foreign keys for this table.
* @return void
*/
protected function initForeignKeys()
{
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
include_once 'creole/metadata/ForeignKeyInfo.php';
if (!@mssql_select_db($this->dbname, $this->conn->getResource())) {
throw new SQLException('No database selected');
}
$res = mssql_query("SELECT ccu1.TABLE_NAME, ccu1.COLUMN_NAME, ccu2.TABLE_NAME AS FK_TABLE_NAME, ccu2.COLUMN_NAME AS FK_COLUMN_NAME
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu1 INNER JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc1 ON tc1.CONSTRAINT_NAME = ccu1.CONSTRAINT_NAME AND
CONSTRAINT_TYPE = 'Foreign Key' INNER JOIN
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc1 ON rc1.CONSTRAINT_NAME = tc1.CONSTRAINT_NAME INNER JOIN
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu2 ON ccu2.CONSTRAINT_NAME = rc1.UNIQUE_CONSTRAINT_NAME
WHERE (ccu1.table_name = '".$this->name."')", $this->conn->getResource());
while($row = mssql_fetch_array($res)) {
$name = $row['COLUMN_NAME'];
$ftbl = $row['FK_TABLE_NAME'];
$fcol = $row['FK_COLUMN_NAME'];
if (!isset($this->foreignKeys[$name])) {
$this->foreignKeys[$name] = new ForeignKeyInfo($name);
if ($this->database->hasTable($ftbl)) {
$foreignTable = $this->database->getTable($ftbl);
} else {
$foreignTable = new TableInfo($ltbl);
$this->database->addTable($foreignTable);
}
if ($foreignTable->hasColumn($fcol)) {
$foreignCol = $foreignTable->getColumn($fcol);
} else {
$foreignCol = new ColumnInfo($foreignTable, $fcol);
$foreignTable->addColumn($foreignCol);
}
$this->foreignKeys[$name]->addReference($this->columns[$name], $foreignCol);
}
}
$this->fksLoaded = true;
}
/**
* Loads the primary key info for this table.
* @return void
*/
protected function initPrimaryKey()
{
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
include_once 'creole/metadata/PrimaryKeyInfo.php';
if (!@mssql_select_db($this->dbname, $this->conn->getResource())) {
throw new SQLException('No database selected');
}
$res = mssql_query("SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ON
INFORMATION_SCHEMA.TABLE_CONSTRAINTS.CONSTRAINT_NAME = INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE.constraint_name
WHERE (INFORMATION_SCHEMA.TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'PRIMARY KEY') AND
(INFORMATION_SCHEMA.TABLE_CONSTRAINTS.TABLE_NAME = '".$this->name."')", $this->conn->getResource());
// Loop through the returned results, grouping the same key_name together.
// name of the primary key will be the first column name in the key.
while($row = mssql_fetch_row($res)) {
$name = $row[0];
if (!isset($this->primaryKey)) {
$this->primaryKey = new PrimaryKeyInfo($name);
}
$this->primaryKey->addColumn($this->columns[ $name ]);
}
$this->pkLoaded = true;
}
}

View File

@ -0,0 +1,290 @@
<?php
/*
* $Id: MySQLConnection.php,v 1.18 2004/09/01 14:00:28 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Connection.php';
require_once 'creole/common/ConnectionCommon.php';
include_once 'creole/drivers/mysql/MySQLResultSet.php';
/**
* MySQL implementation of Connection.
*
*
* @author Hans Lellelid <hans@xmpl.org>
* @author Stig Bakken <ssb@fast.no>
* @author Lukas Smith
* @version $Revision: 1.18 $
* @package creole.drivers.mysql
*/
class MySQLConnection extends ConnectionCommon implements Connection {
/** Current database (used in mysql_select_db()). */
private $database;
/**
* Connect to a database and log in as the specified user.
*
* @param $dsn the data source name (see DB::parseDSN for syntax)
* @param $flags Any conneciton flags.
* @access public
* @throws SQLException
* @return void
*/
function connect($dsninfo, $flags = 0)
{
if (!extension_loaded('mysql')) {
throw new SQLException('mysql extension not loaded');
}
$this->dsn = $dsninfo;
$this->flags = $flags;
$persistent = ($flags & Creole::PERSISTENT) === Creole::PERSISTENT;
if (isset($dsninfo['protocol']) && $dsninfo['protocol'] == 'unix') {
$dbhost = ':' . $dsninfo['socket'];
} else {
$dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
if (!empty($dsninfo['port'])) {
$dbhost .= ':' . $dsninfo['port'];
}
}
$user = $dsninfo['username'];
$pw = $dsninfo['password'];
$encoding = !empty($dsninfo['encoding']) ? $dsninfo['encoding'] : null;
$connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
@ini_set('track_errors', true);
if ($dbhost && $user && $pw) {
$conn = @$connect_function($dbhost, $user, $pw);
} elseif ($dbhost && $user) {
$conn = @$connect_function($dbhost, $user);
} elseif ($dbhost) {
$conn = @$connect_function($dbhost);
} else {
$conn = false;
}
@ini_restore('track_errors');
if (empty($conn)) {
if (($err = @mysql_error()) != '') {
throw new SQLException("connect failed", $err);
} elseif (empty($php_errormsg)) {
throw new SQLException("connect failed");
} else {
throw new SQLException("connect failed", $php_errormsg);
}
}
if ($dsninfo['database']) {
if (!@mysql_select_db($dsninfo['database'], $conn)) {
switch(mysql_errno($conn)) {
case 1049:
$exc = new SQLException("no such database", mysql_error($conn));
break;
case 1044:
$exc = new SQLException("access violation", mysql_error($conn));
break;
default:
$exc = new SQLException("cannot select database", mysql_error($conn));
}
throw $exc;
}
// fix to allow calls to different databases in the same script
$this->database = $dsninfo['database'];
}
$this->dblink = $conn;
if ($encoding) {
$this->executeUpdate("SET NAMES " . $encoding);
}
}
/**
* @see Connection::getDatabaseInfo()
*/
public function getDatabaseInfo()
{
require_once 'creole/drivers/mysql/metadata/MySQLDatabaseInfo.php';
return new MySQLDatabaseInfo($this);
}
/**
* @see Connection::getIdGenerator()
*/
public function getIdGenerator()
{
require_once 'creole/drivers/mysql/MySQLIdGenerator.php';
return new MySQLIdGenerator($this);
}
/**
* @see Connection::prepareStatement()
*/
public function prepareStatement($sql)
{
require_once 'creole/drivers/mysql/MySQLPreparedStatement.php';
return new MySQLPreparedStatement($this, $sql);
}
/**
* @see Connection::prepareCall()
*/
public function prepareCall($sql) {
throw new SQLException('MySQL does not support stored procedures.');
}
/**
* @see Connection::createStatement()
*/
public function createStatement()
{
require_once 'creole/drivers/mysql/MySQLStatement.php';
return new MySQLStatement($this);
}
/**
* @see Connection::disconnect()
*/
function close()
{
$ret = mysql_close($this->dblink);
$this->dblink = null;
return $ret;
}
/**
* @see Connection::applyLimit()
*/
public function applyLimit(&$sql, $offset, $limit)
{
if ( $limit > 0 ) {
$sql .= " LIMIT " . ($offset > 0 ? $offset . ", " : "") . $limit;
} else if ( $offset > 0 ) {
$sql .= " LIMIT " . $offset . ", 18446744073709551615";
}
}
/**
* @see Connection::executeQuery()
*/
function executeQuery($sql, $fetchmode = null)
{
$this->lastQuery = $sql;
if ($this->database) {
if (!@mysql_select_db($this->database, $this->dblink)) {
throw new SQLException('No database selected', mysql_error($this->dblink));
}
}
$result = @mysql_query($sql, $this->dblink);
if (!$result) {
throw new SQLException('Could not execute query', mysql_error($this->dblink), $sql);
}
return new MySQLResultSet($this, $result, $fetchmode);
}
/**
* @see Connection::executeUpdate()
*/
function executeUpdate($sql)
{
$this->lastQuery = $sql;
if ($this->database) {
if (!@mysql_select_db($this->database, $this->dblink)) {
throw new SQLException('No database selected', mysql_error($this->dblink));
}
}
$result = @mysql_query($sql, $this->dblink);
if (!$result) {
throw new SQLException('Could not execute update', mysql_error($this->dblink), $sql);
}
return (int) mysql_affected_rows($this->dblink);
}
/**
* Start a database transaction.
* @throws SQLException
* @return void
*/
protected function beginTrans()
{
$result = @mysql_query('SET AUTOCOMMIT=0', $this->dblink);
$result = @mysql_query('BEGIN', $this->dblink);
if (!$result) {
throw new SQLException('Could not begin transaction', mysql_error($this->dblink));
}
}
/**
* Commit the current transaction.
* @throws SQLException
* @return void
*/
protected function commitTrans()
{
if ($this->database) {
if (!@mysql_select_db($this->database, $this->dblink)) {
throw new SQLException('No database selected', mysql_error($this->dblink));
}
}
$result = @mysql_query('COMMIT', $this->dblink);
$result = @mysql_query('SET AUTOCOMMIT=1', $this->dblink);
if (!$result) {
throw new SQLException('Can not commit transaction', mysql_error($this->dblink));
}
}
/**
* Roll back (undo) the current transaction.
* @throws SQLException
* @return void
*/
protected function rollbackTrans()
{
if ($this->database) {
if (!@mysql_select_db($this->database, $this->dblink)) {
throw new SQLException('No database selected', mysql_error($this->dblink));
}
}
$result = @mysql_query('ROLLBACK', $this->dblink);
$result = @mysql_query('SET AUTOCOMMIT=1', $this->dblink);
if (!$result) {
throw new SQLException('Could not rollback transaction', mysql_error($this->dblink));
}
}
/**
* Gets the number of rows affected by the data manipulation
* query.
*
* @return int Number of rows affected by the last query.
*/
function getUpdateCount()
{
return (int) @mysql_affected_rows($this->dblink);
}
}

View File

@ -0,0 +1,75 @@
<?php
require_once 'creole/IdGenerator.php';
/**
* MySQL IdGenerator implimenation.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.6 $
* @package creole.drivers.mysql
*/
class MySQLIdGenerator implements IdGenerator {
/** Connection object that instantiated this class */
private $conn;
/**
* Creates a new IdGenerator class, saves passed connection for use
* later by getId() method.
* @param Connection $conn
*/
public function __construct(Connection $conn)
{
$this->conn = $conn;
}
/**
* @see IdGenerator::isBeforeInsert()
*/
public function isBeforeInsert()
{
return false;
}
/**
* @see IdGenerator::isAfterInsert()
*/
public function isAfterInsert()
{
return true;
}
/**
* @see IdGenerator::getIdMethod()
*/
public function getIdMethod()
{
return self::AUTOINCREMENT;
}
/**
* Returns last-generated auto-increment ID.
*
* Note that for very large values (2,147,483,648 to 9,223,372,036,854,775,807) a string
* will be returned, because these numbers are larger than supported by PHP's native
* numeric datatypes.
*
* @see IdGenerator::getId()
*/
public function getId($unused = null)
{
$insert_id = mysql_insert_id($this->conn->getResource());
if ( $insert_id < 0 ) {
$insert_id = null;
$result = mysql_query('SELECT LAST_INSERT_ID()', $this->conn->getResource());
if ( $result ) {
$row = mysql_fetch_row($result);
$insert_id = $row ? $row[0] : null;
}
}
return $insert_id;
}
}

View File

@ -0,0 +1,44 @@
<?php
/*
* $Id: MySQLPreparedStatement.php,v 1.7 2005/12/10 13:46:55 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/PreparedStatement.php';
require_once 'creole/common/PreparedStatementCommon.php';
/**
* MySQL subclass for prepared statements.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.7 $
* @package creole.drivers.mysql
*/
class MySQLPreparedStatement extends PreparedStatementCommon implements PreparedStatement {
/**
* Quotes string using native mysql function (mysql_real_escape_string()).
* @param string $str
* @return string
*/
protected function escape($str)
{
return mysql_real_escape_string($str, $this->conn->getResource());
}
}

View File

@ -0,0 +1,149 @@
<?php
/*
* $Id: MySQLResultSet.php,v 1.24 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/ResultSet.php';
require_once 'creole/common/ResultSetCommon.php';
/**
* MySQL implementation of ResultSet class.
*
* MySQL supports OFFSET / LIMIT natively; this means that no adjustments or checking
* are performed. We will assume that if the lmitSQL() operation failed that an
* exception was thrown, and that OFFSET/LIMIT will never be emulated for MySQL.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.24 $
* @package creole.drivers.mysql
*/
class MySQLResultSet extends ResultSetCommon implements ResultSet {
/**
* @see ResultSet::seek()
*/
public function seek($rownum)
{
// MySQL rows start w/ 0, but this works, because we are
// looking to move the position _before_ the next desired position
if (!@mysql_data_seek($this->result, $rownum)) {
return false;
}
$this->cursorPos = $rownum;
return true;
}
/**
* @see ResultSet::next()
*/
public function next()
{
$this->fields = mysql_fetch_array($this->result, $this->fetchmode);
if (!$this->fields) {
$errno = mysql_errno($this->conn->getResource());
if (!$errno) {
// We've advanced beyond end of recordset.
$this->afterLast();
return false;
} else {
throw new SQLException("Error fetching result", mysql_error($this->conn->getResource()));
}
}
if ($this->fetchmode === ResultSet::FETCHMODE_ASSOC && $this->lowerAssocCase) {
$this->fields = array_change_key_case($this->fields, CASE_LOWER);
}
// Advance cursor position
$this->cursorPos++;
return true;
}
/**
* @see ResultSet::getRecordCount()
*/
function getRecordCount()
{
$rows = @mysql_num_rows($this->result);
if ($rows === null) {
throw new SQLException("Error fetching num rows", mysql_error($this->conn->getResource()));
}
return (int) $rows;
}
/**
* @see ResultSet::close()
*/
function close()
{
if(is_resource($this->result))
@mysql_free_result($this->result);
$this->fields = array();
}
/**
* Get string version of column.
* No rtrim() necessary for MySQL, as this happens natively.
* @see ResultSet::getString()
*/
public function getString($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
return (string) $this->fields[$idx];
}
/**
* Returns a unix epoch timestamp based on either a TIMESTAMP or DATETIME field.
* @param mixed $column Column name (string) or index (int) starting with 1.
* @return string
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
function getTimestamp($column, $format='Y-m-d H:i:s')
{
if (is_int($column)) { $column--; } // because Java convention is to start at 1
if (!array_key_exists($column, $this->fields)) { throw new SQLException("Invalid resultset column: " . (is_int($column) ? $column + 1 : $column)); }
if ($this->fields[$column] === null) { return null; }
$ts = strtotime($this->fields[$column]);
if ($ts === -1 || $ts === false) { // in PHP 5.1 return value changes to FALSE
// otherwise it's an ugly MySQL timestamp!
// YYYYMMDDHHMMSS
if (preg_match('/([\d]{4})([\d]{2})([\d]{2})([\d]{2})([\d]{2})([\d]{2})/', $this->fields[$column], $matches)) {
// YYYY MM DD HH MM SS
// $1 $2 $3 $4 $5 $6
$ts = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
}
}
if ($ts === -1 || $ts === false) { // if it's still -1, then there's nothing to be done; use a different method.
throw new SQLException("Unable to convert value at column " . (is_int($column) ? $column + 1 : $column) . " to timestamp: " . $this->fields[$column]);
}
if ($format === null) {
return $ts;
}
if (strpos($format, '%') !== false) {
return strftime($format, $ts);
} else {
return date($format, $ts);
}
}
}

View File

@ -0,0 +1,36 @@
<?php
/*
* $Id: MySQLStatement.php,v 1.1 2004/02/19 02:49:42 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Statement.php';
require_once 'creole/common/StatementCommon.php';
/**
* MySQL Statement
*
* @author Hans Lellelid <hans@xmpl.org>
* @author Stig Bakken <ssb@fast.no>
* @author Lukas Smith
* @version $Revision: 1.1 $
* @package creole.drivers.mysql
*/
class MySQLStatement extends StatementCommon implements Statement {
}

View File

@ -0,0 +1,102 @@
<?php
/*
* $Id: MySQLTypes.php,v 1.8 2005/02/10 09:22:40 pachanga Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/CreoleTypes.php';
/**
* MySQL types / type map.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.8 $
* @package creole.drivers.mysql
*/
class MySQLTypes extends CreoleTypes {
/** Map MySQL native types to Creole (JDBC) types. */
private static $typeMap = array(
'tinyint' => CreoleTypes::TINYINT,
'smallint' => CreoleTypes::SMALLINT,
'mediumint' => CreoleTypes::SMALLINT,
'int' => CreoleTypes::INTEGER,
'integer' => CreoleTypes::INTEGER,
'bigint' => CreoleTypes::BIGINT,
'int24' => CreoleTypes::BIGINT,
'real' => CreoleTypes::REAL,
'float' => CreoleTypes::FLOAT,
'decimal' => CreoleTypes::DECIMAL,
'numeric' => CreoleTypes::NUMERIC,
'double' => CreoleTypes::DOUBLE,
'char' => CreoleTypes::CHAR,
'varchar' => CreoleTypes::VARCHAR,
'date' => CreoleTypes::DATE,
'time' => CreoleTypes::TIME,
'year' => CreoleTypes::YEAR,
'datetime' => CreoleTypes::TIMESTAMP,
'timestamp' => CreoleTypes::TIMESTAMP,
'tinyblob' => CreoleTypes::BINARY,
'blob' => CreoleTypes::VARBINARY,
'mediumblob' => CreoleTypes::VARBINARY,
'longblob' => CreoleTypes::VARBINARY,
'longtext' => CreoleTypes::LONGVARCHAR,
'tinytext' => CreoleTypes::VARCHAR,
'mediumtext' => CreoleTypes::LONGVARCHAR,
'text' => CreoleTypes::LONGVARCHAR,
'enum' => CreoleTypes::CHAR,
'set' => CreoleTypes::CHAR,
);
/** Reverse mapping, created on demand. */
private static $reverseMap = null;
/**
* This method returns the generic Creole (JDBC-like) type
* when given the native db type.
* @param string $nativeType DB native type (e.g. 'TEXT', 'byetea', etc.).
* @return int Creole native type (e.g. CreoleTypes::LONGVARCHAR, CreoleTypes::BINARY, etc.).
*/
public static function getType($nativeType)
{
$t = strtolower($nativeType);
if (isset(self::$typeMap[$t])) {
return self::$typeMap[$t];
} else {
return CreoleTypes::OTHER;
}
}
/**
* This method will return a native type that corresponds to the specified
* Creole (JDBC-like) type.
* If there is more than one matching native type, then the LAST defined
* native type will be returned.
* @param int $creoleType
* @return string Native type string.
*/
public static function getNativeType($creoleType)
{
if (self::$reverseMap === null) {
self::$reverseMap = array_flip(self::$typeMap);
}
return @self::$reverseMap[$creoleType];
}
}

View File

@ -0,0 +1,66 @@
<?php
/*
* $Id: MySQLDatabaseInfo.php,v 1.13 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/DatabaseInfo.php';
/**
* MySQL implementation of DatabaseInfo.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.13 $
* @package creole.drivers.mysql.metadata
*/
class MySQLDatabaseInfo extends DatabaseInfo {
/**
* @throws SQLException
* @return void
*/
protected function initTables()
{
include_once 'creole/drivers/mysql/metadata/MySQLTableInfo.php';
// using $this->dblink was causing tests to break
// perhaps dblink is changed by another test ... ?
$result = @mysql_query("SHOW TABLES FROM `" . $this->dbname . "`", $this->conn->getResource());
if (!$result) {
throw new SQLException("Could not list tables", mysql_error($this->conn->getResource()));
}
while ($row = mysql_fetch_row($result)) {
$this->tables[strtoupper($row[0])] = new MySQLTableInfo($this, $row[0]);
}
$this->tablesLoaded = true;
}
/**
* MySQL does not support sequences.
*
* @return void
* @throws SQLException
*/
protected function initSequences()
{
// throw new SQLException("MySQL does not support sequences natively.");
}
}

View File

@ -0,0 +1,252 @@
<?php
/*
* $Id: MySQLTableInfo.php,v 1.20 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/TableInfo.php';
/**
* MySQL implementation of TableInfo.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.20 $
* @package creole.drivers.mysql.metadata
*/
class MySQLTableInfo extends TableInfo {
/** Loads the columns for this table. */
protected function initColumns()
{
include_once 'creole/metadata/ColumnInfo.php';
include_once 'creole/drivers/mysql/MySQLTypes.php';
if (!@mysql_select_db($this->dbname, $this->conn->getResource())) {
throw new SQLException('No database selected');
}
// To get all of the attributes we need, we use
// the MySQL "SHOW COLUMNS FROM $tablename" SQL. We cannot
// use the API functions (e.g. mysql_list_fields() because they
// do not return complete information -- e.g. precision / scale, default
// values).
$res = mysql_query("SHOW COLUMNS FROM `" . $this->name . "`", $this->conn->getResource());
$defaults = array();
$nativeTypes = array();
$precisions = array();
while($row = mysql_fetch_assoc($res)) {
$name = $row['Field'];
$is_nullable = ($row['Null'] == 'YES');
$is_auto_increment = (strpos($row['Extra'], 'auto_increment') !== false);
$size = null;
$precision = null;
$scale = null;
if (preg_match('/^(\w+)[\(]?([\d,]*)[\)]?( |$)/', $row['Type'], $matches)) {
// colname[1] size/precision[2]
$nativeType = $matches[1];
if ($matches[2]) {
if ( ($cpos = strpos($matches[2], ',')) !== false) {
$size = (int) substr($matches[2], 0, $cpos);
$precision = $size;
$scale = (int) substr($matches[2], $cpos + 1);
} else {
$size = (int) $matches[2];
}
}
} elseif (preg_match('/^(\w+)\(/', $row['Type'], $matches)) {
$nativeType = $matches[1];
} else {
$nativeType = $row['Type'];
}
//BLOBs can't have any default values in MySQL
$default = preg_match('~blob|text~', $nativeType) ? null : $row['Default'];
$this->columns[$name] = new ColumnInfo($this,
$name,
MySQLTypes::getType($nativeType),
$nativeType,
$size,
$precision,
$scale,
$is_nullable,
$default,
$is_auto_increment,
$row);
}
$this->colsLoaded = true;
}
/** Loads the primary key information for this table. */
protected function initPrimaryKey()
{
include_once 'creole/metadata/PrimaryKeyInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
if (!@mysql_select_db($this->dbname, $this->conn->getResource())) {
throw new SQLException('No database selected');
}
// Primary Keys
$res = mysql_query("SHOW KEYS FROM `" . $this->name . "`", $this->conn->getResource());
// Loop through the returned results, grouping the same key_name together
// adding each column for that key.
while($row = mysql_fetch_assoc($res)) {
// Skip any non-primary keys.
if ($row['Key_name'] !== 'PRIMARY') {
continue;
}
$name = $row["Column_name"];
if (!isset($this->primaryKey)) {
$this->primaryKey = new PrimaryKeyInfo($name, $row);
}
$this->primaryKey->addColumn($this->columns[$name]);
}
$this->pkLoaded = true;
}
/** Loads the indexes for this table. */
protected function initIndexes() {
include_once 'creole/metadata/IndexInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
if (!@mysql_select_db($this->dbname, $this->conn->getResource())) {
throw new SQLException('No database selected');
}
// Indexes
$res = mysql_query("SHOW INDEX FROM `" . $this->name . "`", $this->conn->getResource());
// Loop through the returned results, grouping the same key_name together
// adding each column for that key.
while($row = mysql_fetch_assoc($res)) {
$colName = $row["Column_name"];
$name = $row["Key_name"];
if($name == "PRIMARY") {
continue;
}
if (!isset($this->indexes[$name])) {
$isUnique = ($row["Non_unique"] == 0);
$this->indexes[$name] = new IndexInfo($name, $isUnique, $row);
}
$this->indexes[$name]->addColumn($this->columns[$colName]);
}
$this->indexesLoaded = true;
}
/**
* Load foreign keys for supporting versions of MySQL.
* @author Tony Bibbs
*/
protected function initForeignKeys() {
// First make sure we have supported version of MySQL:
$res = mysql_query("SELECT VERSION()");
$row = mysql_fetch_row($res);
// Yes, it is OK to hardcode this...this was the first version of MySQL
// that supported foreign keys
if ($row[0] < '3.23.44') {
$this->fksLoaded = true;
return;
}
include_once 'creole/metadata/ForeignKeyInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
if (!@mysql_select_db($this->dbname, $this->conn->getResource())) {
throw new SQLException('No database selected');
}
// Get the CREATE TABLE syntax
$res = mysql_query("SHOW CREATE TABLE `" . $this->name . "`", $this->conn->getResource());
$row = mysql_fetch_row($res);
// Get the information on all the foreign keys
$regEx = '/FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)(.*)/';
if (preg_match_all($regEx,$row[1],$matches)) {
$tmpArray = array_keys($matches[0]);
foreach ($tmpArray as $curKey) {
$name = $matches[1][$curKey];
$ftbl = $matches[2][$curKey];
$fcol = $matches[3][$curKey];
$fkey = $matches[4][$curKey];
if (!isset($this->foreignKeys[$name])) {
$this->foreignKeys[$name] = new ForeignKeyInfo($name);
if ($this->database->hasTable($ftbl)) {
$foreignTable = $this->database->getTable($ftbl);
} else {
$foreignTable = new MySQLTableInfo($this->database, $ftbl);
$this->database->addTable($foreignTable);
}
if ($foreignTable->hasColumn($fcol)) {
$foreignCol = $foreignTable->getColumn($fcol);
} else {
$foreignCol = new ColumnInfo($foreignTable, $fcol);
$foreignTable->addColumn($foreignCol);
}
//typical for mysql is RESTRICT
$fkactions = array(
'ON DELETE' => ForeignKeyInfo::RESTRICT,
'ON UPDATE' => ForeignKeyInfo::RESTRICT,
);
if ($fkey) {
//split foreign key information -> search for ON DELETE and afterwords for ON UPDATE action
foreach (array_keys($fkactions) as $fkaction) {
$result = NULL;
preg_match('/' . $fkaction . ' (' . ForeignKeyInfo::CASCADE . '|' . ForeignKeyInfo::SETNULL . ')/', $fkey, $result);
if ($result && is_array($result) && isset($result[1])) {
$fkactions[$fkaction] = $result[1];
}
}
}
$this->foreignKeys[$name]->addReference($this->columns[$name], $foreignCol, $fkactions['ON DELETE'], $fkactions['ON UPDATE']);
}
}
}
$this->fksLoaded = true;
}
protected function initVendorSpecificInfo()
{
$res = mysql_query("SHOW TABLE STATUS LIKE '" . $this->name . "'", $this->conn->getResource());
$this->vendorSpecificInfo = mysql_fetch_assoc($res);
$this->vendorLoaded = true;
}
}

View File

@ -0,0 +1,293 @@
<?php
/*
* $Id: MySQLiConnection.php,v 1.7 2004/09/18 09:29:22 sb Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Connection.php';
require_once 'creole/common/ConnectionCommon.php';
include_once 'creole/drivers/mysqli/MySQLiResultSet.php';
/**
* MySQLi implementation of Connection.
*
*
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @version $Revision: 1.7 $
* @package creole.drivers.mysqli
*/
class MySQLiConnection extends ConnectionCommon implements Connection {
/** Current database (used in mysqli_select_db()). */
private $database;
/**
* Connect to a database and log in as the specified user.
*
* @param $dsn the data source name (see DB::parseDSN for syntax)
* @param $flags Any conneciton flags.
* @access public
* @throws SQLException
* @return void
*/
public function connect($dsninfo, $flags = 0)
{
if (!extension_loaded('mysqli')) {
throw new SQLException('mysqli extension not loaded');
}
$this->dsn = $dsninfo;
$this->flags = $flags;
$dbhost = null;
if (isset($dsninfo['protocol']) && $dsninfo['protocol'] == 'unix') {
$dbhost = ':' . $dsninfo['socket'];
} else {
$dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
if (!empty($dsninfo['port'])) {
$dbhost .= ':' . $dsninfo['port'];
}
}
$host = !empty($dsninfo['hostspec']) ? $dsninfo['hostspec'] : null;
$user = !empty($dsninfo['username']) ? $dsninfo['username'] : null;
$pw = !empty($dsninfo['password']) ? $dsninfo['password'] : null;
$port = !empty($dsninfo['port']) ? $dsninfo['port'] : null;
$socket = !empty($dsninfo['socket']) ? $dsninfo['socket'] : null;
$database = !empty($dsninfo['database']) ? $dsninfo['database'] : null;
$encoding = !empty($dsninfo['encoding']) ? $dsninfo['encoding'] : null;
@ini_set('track_errors', true);
$conn = mysqli_connect($host, $user, $pw, $database, $port, $socket);
@ini_restore('track_errors');
if (empty($conn)) {
if (($err = @mysqli_error()) != '') {
throw new SQLException("connect failed", $err);
} elseif (empty($php_errormsg)) {
throw new SQLException("connect failed");
} else {
throw new SQLException("connect failed", $php_errormsg);
}
}
if ($dsninfo['database']) {
if (!@mysqli_select_db($conn, $dsninfo['database'])) {
switch(mysqli_errno($conn)) {
case 1049:
$exc = new SQLException("no such database", mysqli_error($conn));
break;
case 1044:
$exc = new SQLException("access violation", mysqli_error($conn));
break;
default:
$exc = new SQLException("cannot select database", mysqli_error($conn));
}
throw $exc;
}
// fix to allow calls to different databases in the same script
$this->database = $dsninfo['database'];
}
$this->dblink = $conn;
if ($encoding) {
$this->executeUpdate("SET NAMES " . $encoding);
}
}
/**
* @see Connection::getDatabaseInfo()
*/
public function getDatabaseInfo()
{
require_once 'creole/drivers/mysqli/metadata/MySQLiDatabaseInfo.php';
return new MySQLiDatabaseInfo($this);
}
/**
* @see Connection::getIdGenerator()
*/
public function getIdGenerator()
{
require_once 'creole/drivers/mysqli/MySQLiIdGenerator.php';
return new MySQLiIdGenerator($this);
}
/**
* @see Connection::prepareStatement()
*/
public function prepareStatement($sql)
{
require_once 'creole/drivers/mysqli/MySQLiPreparedStatement.php';
return new MySQLiPreparedStatement($this, $sql);
}
/**
* @see Connection::prepareCall()
*/
public function prepareCall($sql) {
throw new SQLException('MySQL does not support stored procedures.');
}
/**
* @see Connection::createStatement()
*/
public function createStatement()
{
require_once 'creole/drivers/mysqli/MySQLiStatement.php';
return new MySQLiStatement($this);
}
/**
* @see Connection::disconnect()
*/
public function close()
{
$ret = mysqli_close($this->dblink);
$this->dblink = null;
return $ret;
}
/**
* @see Connection::applyLimit()
*/
public function applyLimit(&$sql, $offset, $limit)
{
if ( $limit > 0 ) {
$sql .= " LIMIT " . ($offset > 0 ? $offset . ", " : "") . $limit;
} else if ( $offset > 0 ) {
$sql .= " LIMIT " . $offset . ", 18446744073709551615";
}
}
/**
* @see Connection::executeQuery()
*/
public function executeQuery($sql, $fetchmode = null)
{
$this->lastQuery = $sql;
if ($this->database) {
if (!@mysqli_select_db($this->dblink, $this->database)) {
throw new SQLException('No database selected', mysqli_error($this->dblink));
}
}
$result = @mysqli_query($this->dblink, $sql);
if (!$result) {
throw new SQLException('Could not execute query', mysqli_error($this->dblink), $sql);
}
return new MySQLiResultSet($this, $result, $fetchmode);
}
/**
* @see Connection::executeUpdate()
*/
public function executeUpdate($sql)
{
$this->lastQuery = $sql;
if ($this->database) {
if (!@mysqli_select_db($this->dblink, $this->database)) {
throw new SQLException('No database selected', mysqli_error($this->dblink));
}
}
$result = @mysqli_query($this->dblink, $sql);
if (!$result) {
throw new SQLException('Could not execute update', mysqli_error($this->dblink), $sql);
}
return (int) mysqli_affected_rows($this->dblink);
}
/**
* Start a database transaction.
* @throws SQLException
* @return void
*/
protected function beginTrans()
{
if (!mysqli_autocommit($this->dblink, FALSE)) {
throw new SQLException('Could not begin transaction', mysqli_error($this->dblink));
}
}
/**
* Commit the current transaction.
* @throws SQLException
* @return void
*/
protected function commitTrans()
{
if ($this->database) {
if (!@mysqli_select_db($this->dblink, $this->database)) {
throw new SQLException('No database selected', mysqli_error($this->dblink));
}
}
if (!mysqli_commit($this->dblink)) {
throw new SQLException('Can not commit transaction', mysqli_error($this->dblink));
}
mysqli_autocommit($this->dblink, TRUE);
}
/**
* Roll back (undo) the current transaction.
* @throws SQLException
* @return void
*/
protected function rollbackTrans()
{
if ($this->database) {
if (!@mysqli_select_db($this->dblink, $this->database)) {
throw new SQLException('No database selected', mysqli_error($this->dblink));
}
}
if (!mysqli_rollback($this->dblink)) {
throw new SQLException('Could not rollback transaction', mysqli_error($this->dblink));
}
mysqli_autocommit($this->dblink, TRUE);
}
/**
* Gets the number of rows affected by the data manipulation
* query.
*
* @return int Number of rows affected by the last query.
*/
public function getUpdateCount()
{
return (int) @mysqli_affected_rows($this->dblink);
}
}

View File

@ -0,0 +1,96 @@
<?php
/*
* $Id: MySQLiIdGenerator.php,v 1.4 2004/09/18 09:15:49 sb Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/IdGenerator.php';
/**
* MySQLi implementation of IdGenerator.
*
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @version $Revision: 1.4 $
* @package creole.drivers.mysqli
*/
class MySQLiIdGenerator implements IdGenerator {
/** Connection object that instantiated this class */
private $conn;
/**
* Creates a new IdGenerator class, saves passed connection for use
* later by getId() method.
* @param Connection $conn
*/
public function __construct(Connection $conn)
{
$this->conn = $conn;
}
/**
* @see IdGenerator::isBeforeInsert()
*/
public function isBeforeInsert()
{
return false;
}
/**
* @see IdGenerator::isAfterInsert()
*/
public function isAfterInsert()
{
return true;
}
/**
* @see IdGenerator::getIdMethod()
*/
public function getIdMethod()
{
return self::AUTOINCREMENT;
}
/**
* Returns last-generated auto-increment ID.
*
* Note that for very large values (2,147,483,648 to 9,223,372,036,854,775,807) a string
* will be returned, because these numbers are larger than supported by PHP's native
* numeric datatypes.
*
* @see IdGenerator::getId()
*/
public function getId($unused = null)
{
$resource = $this->conn->getResource();
$insert_id = mysqli_insert_id($resource);
if ( $insert_id < 0 ) {
$insert_id = null;
$result = mysqli_query($resource, 'SELECT LAST_INSERT_ID()');
if ( $result ) {
$row = mysqli_fetch_row($result);
$insert_id = $row ? $row[0] : null;
}
}
return $insert_id;
}
}

View File

@ -0,0 +1,42 @@
<?php
/*
* $Id: MySQLiPreparedStatement.php,v 1.3 2004/09/18 09:15:49 sb Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/PreparedStatement.php';
require_once 'creole/common/PreparedStatementCommon.php';
/**
* MySQLi implementation of PreparedStatement.
*
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @version $Revision: 1.3 $
* @package creole.drivers.mysqli
*/
class MySQLiPreparedStatement extends PreparedStatementCommon implements PreparedStatement {
/**
* Quotes string using native MySQL function.
* @param string $str
* @return string
*/
protected function escape($str)
{
return mysqli_real_escape_string($this->getConnection()->getResource(), $str);
}
}

View File

@ -0,0 +1,173 @@
<?php
/*
* $Id: MySQLiResultSet.php,v 1.5 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/ResultSet.php';
require_once 'creole/common/ResultSetCommon.php';
/**
* MySQLi implementation of ResultSet.
*
* MySQL supports OFFSET / LIMIT natively; this means that no adjustments or checking
* are performed. We will assume that if the lmitSQL() operation failed that an
* exception was thrown, and that OFFSET/LIMIT will never be emulated for MySQL.
*
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @version $Revision: 1.5 $
* @package creole.drivers.mysqli
*/
class MySQLiResultSet extends ResultSetCommon implements ResultSet {
/**
* @see ResultSet::seek()
*/
public function seek($rownum)
{
// MySQL rows start w/ 0, but this works, because we are
// looking to move the position _before_ the next desired position
if (!@mysqli_data_seek($this->result, $rownum)) {
return false;
}
$this->cursorPos = $rownum;
return true;
}
/**
* @see ResultSet::next()
*/
public function next()
{
$this->fields = mysqli_fetch_array($this->result, $this->fetchmode);
$resource = $this->conn->getResource();
if (!$this->fields) {
$errno = mysqli_errno($resource);
if (!$errno) {
// We've advanced beyond end of recordset.
$this->afterLast();
return false;
} else {
throw new SQLException("Error fetching result", mysqli_error($resource));
}
}
if ($this->fetchmode === ResultSet::FETCHMODE_ASSOC && $this->lowerAssocCase) {
$this->fields = array_change_key_case($this->fields, CASE_LOWER);
}
// Advance cursor position
$this->cursorPos++;
return true;
}
/**
* @see ResultSet::getRecordCount()
*/
public function getRecordCount()
{
$rows = @mysqli_num_rows($this->result);
if ($rows === null) {
throw new SQLException("Error fetching num rows", mysqli_error($this->conn->getResource()));
}
return (int) $rows;
}
/**
* @see ResultSet::close()
*/
public function close()
{
@mysqli_free_result($this->result);
$this->fields = array();
}
/**
* Get string version of column.
* No rtrim() necessary for MySQL, as this happens natively.
* @see ResultSet::getString()
*/
public function getString($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) {
throw new SQLException("Invalid resultset column: " . $column);
}
if ($this->fields[$idx] === null) {
return null;
}
return (string) $this->fields[$idx];
}
/**
* Returns a unix epoch timestamp based on either a TIMESTAMP or DATETIME field.
* @param mixed $column Column name (string) or index (int) starting with 1.
* @return string
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getTimestamp($column, $format='Y-m-d H:i:s')
{
if (is_int($column)) {
// because Java convention is to start at 1
$column--;
}
if (!array_key_exists($column, $this->fields)) {
throw new SQLException("Invalid resultset column: " . (is_int($column) ? $column + 1 : $column));
}
if ($this->fields[$column] === null) {
return null;
}
$ts = strtotime($this->fields[$column]);
if ($ts === -1 || $ts === false) { // in PHP 5.1 return value changes to FALSE
// otherwise it's an ugly MySQL timestamp!
// YYYYMMDDHHMMSS
if (preg_match('/([\d]{4})([\d]{2})([\d]{2})([\d]{2})([\d]{2})([\d]{2})/', $this->fields[$column], $matches)) {
// YYYY MM DD HH MM SS
// $1 $2 $3 $4 $5 $6
$ts = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
}
}
if ($ts === -1 || $ts === false) { // in PHP 5.1 return value changes to FALSE
// if it's still -1, then there's nothing to be done; use a different method.
throw new SQLException("Unable to convert value at column " . (is_int($column) ? $column + 1 : $column) . " to timestamp: " . $this->fields[$column]);
}
if ($format === null) {
return $ts;
}
if (strpos($format, '%') !== false) {
return strftime($format, $ts);
} else {
return date($format, $ts);
}
}
}

View File

@ -0,0 +1,33 @@
<?php
/*
* $Id: MySQLiStatement.php,v 1.2 2004/09/18 09:15:49 sb Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Statement.php';
require_once 'creole/common/StatementCommon.php';
/**
* MySQLi implementation of Statement.
*
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @version $Revision: 1.2 $
* @package creole.drivers.mysqli
*/
class MySQLiStatement extends StatementCommon implements Statement {
}

View File

@ -0,0 +1,61 @@
<?php
/*
* $Id: MySQLiDatabaseInfo.php,v 1.3 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/DatabaseInfo.php';
/**
* MySQLi implementation of DatabaseInfo.
*
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @version $Revision: 1.3 $
* @package creole.drivers.mysqli.metadata
*/
class MySQLiDatabaseInfo extends DatabaseInfo {
/**
* @throws SQLException
* @return void
*/
protected function initTables()
{
include_once 'creole/drivers/mysqli/metadata/MySQLiTableInfo.php';
$result = @mysqli_query($this->conn->getResource(), 'SHOW TABLES FROM ' . $this->dbname);
if (!$result) {
throw new SQLException("Could not list tables", mysqli_error($this->conn->getResource()));
}
while ($row = mysqli_fetch_row($result)) {
$this->tables[strtoupper($row[0])] = new MySQLiTableInfo($this, $row[0]);
}
}
/**
* MySQL does not support sequences.
*
* @return void
* @throws SQLException
*/
protected function initSequences()
{
// throw new SQLException("MySQL does not support sequences natively.");
}
}

View File

@ -0,0 +1,155 @@
<?php
/*
* $Id: MySQLiTableInfo.php,v 1.3 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/TableInfo.php';
/**
* MySQLi implementation of TableInfo.
*
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @version $Revision: 1.3 $
* @package creole.drivers.mysqli.metadata
*/
class MySQLiTableInfo extends TableInfo {
/** Loads the columns for this table. */
protected function initColumns()
{
require_once 'creole/metadata/ColumnInfo.php';
require_once 'creole/drivers/mysql/MySQLTypes.php';
if (!@mysqli_select_db($this->conn->getResource(), $this->dbname)) {
throw new SQLException('No database selected');
}
// To get all of the attributes we need, we use
// the MySQL "SHOW COLUMNS FROM $tablename" SQL.
$res = mysqli_query($this->conn->getResource(), "SHOW COLUMNS FROM " . $this->name);
$defaults = array();
$nativeTypes = array();
$precisions = array();
while($row = mysqli_fetch_assoc($res)) {
$name = $row['Field'];
$default = $row['Default'];
$is_nullable = ($row['Null'] == 'YES');
$size = null;
$precision = null;
$scale = null;
if (preg_match('/^(\w+)[\(]?([\d,]*)[\)]?( |$)/', $row['Type'], $matches)) {
// colname[1] size/precision[2]
$nativeType = $matches[1];
if ($matches[2]) {
if ( ($cpos = strpos($matches[2], ',')) !== false) {
$size = (int) substr($matches[2], 0, $cpos);
$precision = $size;
$scale = (int) substr($matches[2], $cpos + 1);
} else {
$size = (int) $matches[2];
}
}
} elseif (preg_match('/^(\w+)\(/', $row['Type'], $matches)) {
$nativeType = $matches[1];
} else {
$nativeType = $row['Type'];
}
$this->columns[$name] = new ColumnInfo($this, $name, MySQLTypes::getType($nativeType), $nativeType, $size, $precision, $scale, $is_nullable, $default);
}
$this->colsLoaded = true;
}
/** Loads the primary key information for this table. */
protected function initPrimaryKey()
{
require_once 'creole/metadata/PrimaryKeyInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) {
$this->initColumns();
}
if (!@mysqli_select_db($this->conn->getResource(), $this->dbname)) {
throw new SQLException('No database selected');
}
// Primary Keys
$res = mysqli_query($this->conn->getResource(), "SHOW KEYS FROM " . $this->name);
// Loop through the returned results, grouping the same key_name together
// adding each column for that key.
while($row = mysqli_fetch_assoc($res)) {
$name = $row["Column_name"];
if (!isset($this->primaryKey)) {
$this->primaryKey = new PrimaryKeyInfo($name);
}
$this->primaryKey->addColumn($this->columns[ $name ]);
}
$this->pkLoaded = true;
}
/** Loads the indexes for this table. */
protected function initIndexes() {
require_once 'creole/metadata/IndexInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) {
$this->initColumns();
}
if (!@mysqli_select_db($this->conn->getResource(), $this->dbname)) {
throw new SQLException('No database selected');
}
// Indexes
$res = mysqli_query($this->conn->getResource(), "SHOW INDEX FROM " . $this->name);
// Loop through the returned results, grouping the same key_name together
// adding each column for that key.
while($row = mysqli_fetch_assoc($res)) {
$name = $row["Column_name"];
if (!isset($this->indexes[$name])) {
$this->indexes[$name] = new IndexInfo($name);
}
$this->indexes[$name]->addColumn($this->columns[ $name ]);
}
$this->indexesLoaded = true;
}
/** Load foreign keys (unsupported in MySQL). */
protected function initForeignKeys() {
// columns have to be loaded first
if (!$this->colsLoaded) {
$this->initColumns();
}
// Foreign keys are not supported in mysql.
$this->fksLoaded = true;
}
}

View File

@ -0,0 +1,218 @@
<?php
/*
* $Id: ODBCCachedResultSet.php,v 1.2 2005/04/01 17:04:00 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/drivers/odbc/ODBCResultSetCommon.php';
require_once 'creole/drivers/odbc/ODBCTypes.php';
/**
* ODBC implementation of a cached ResultSet.
*
* In addition to limit/offset emulation, this class implements a resultset
* cache. This can be useful as a workaround for some ODBC drivers which lack
* support for reverse/absolute cursor scrolling, etc.
*
* This class will cache rows _on-demand_. So if you only read the first couple
* rows of a result, then only those rows will be cached. However, note that if
* you call getRecordCount() or last(), the class must read and cache all
* available records.
*
* The offset / limit variables are also taken into account when caching. Any
* rows preceding the offset value will be skipped. Caching will stop once the
* limit value is reached.
*
* To use this class, create a derived {@link ODBCAdapter} class which returns
* an instance of ODBCCachedResultSet from the {@link ODBCAdapter::createResultSet()} method.
* Specify the adapter via the query portion of the Connection URL:
*
* odbc://localhost/Driver=MySQL ODBC 3.51 Driver;Database=test?adapter=MySQL
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.2 $
* @package creole.drivers.odbc
*/
class ODBCCachedResultSet extends ODBCResultSetCommon implements ResultSet
{
/**
* Record cache
* @var array
*/
protected $recs = array();
/**
* Tracks the last cursor position of the recordset.
* @var integer
*/
protected $lastPos = -1;
/**
* True if blobs/clobs should also be cached.
* @var boolean
*/
protected $cacheLobs = false;
/**
* @see ResultSet::__construct()
*/
public function __construct(Connection $conn, $result, $fetchmode = null, $cacheLobs = false)
{
parent::__construct($conn, $result, $fetchmode);
$this->cacheLobs = $cacheLobs;
}
/**
* @see ODBCResultSetCommon::close()
*/
function close()
{
parent::close();
$this->recs = null;
$this->lastPos = -1;
$this->cacheLobs = false;
}
/**
* Caches specified records up to and including the specified 1-based
* record position. If -1 is specified, all records will be cached.
* @param integer Maximum record position to cache.
* @return void
* @throws SQLException
*/
public function loadCache($recPos = -1)
{
$rid = $this->result->getHandle();
$curRecs = count($this->recs);
$totRecs = ($curRecs ? $this->offset + $curRecs : 0);
while (1)
{
// Is record already cached?
if ($this->lastPos != -1 || ($recPos > -1 && $recPos <= $curRecs))
return;
// Fetch row (no buffers copied yet).
$rowNum = ++$totRecs;
$result = @odbc_fetch_row($rid, $rowNum);
// All records cached?
if ($result === false || ($this->limit > 0 && $curRecs+1 > $this->limit))
{
$this->lastPos = $curRecs;
continue;
}
// Ignore offset records.
if ($totRecs <= $this->offset)
continue;
// Load row array.
$row = array();
for ($i = 0, $n = @odbc_num_fields($rid); $i < $n; $i++)
{
$fldNum = $i+1;
$row[$i] = odbc_result($rid, $fldNum);
// Cache lobs if necessary
if ($this->cacheLobs)
{
ODBCTypes::loadTypeMap($this->conn);
$nativeType = @odbc_field_type($rid, $fldNum);
$creoleType = ODBCTypes::getType($nativeType);
$isBlob = ($creoleType == CreoleTypes::BLOB ||
$creoleType == CreoleTypes::LONGVARBINARY);
$isClob = ($creoleType == CreoleTypes::CLOB ||
$creoleType == CreoleTypes::LONGVARCHAR);
if (($isBlob || $isClob) && $row[$i] !== null)
{
$binmode = ($isBlob ? ODBC_BINMODE_RETURN : ODBC_BINMODE_CONVERT);
$curdata = $row[$i];
$row[$i] = $this->readLobData($fldNum, $binmode, $curdata);
}
}
}
// Add record to cache.
$this->recs[++$curRecs] = $row;
}
}
/**
* @see ResultSet::seek()
*/
public function seek($rownum)
{
$this->loadCache($rownum);
if ($rownum < 0 || $rownum > count($this->recs)+1)
return false;
$this->cursorPos = $rownum;
return true;
}
/**
* @see ResultSet::next()
*/
function next()
{
$this->loadCache(++$this->cursorPos);
if ($this->isAfterLast())
{
$this->afterLast();
return false;
}
$this->fields =& $this->checkFetchMode($this->recs[$this->cursorPos]);
return true;
}
/**
* @see ResultSet::getRecordCount()
*/
function getRecordCount()
{
if ($this->lastPos == -1)
$this->loadCache(-1);
return $this->lastPos;
}
/**
* @see ResultSet::isAfterLast()
*/
public function isAfterLast()
{
// All records cached yet?
if ($this->lastPos == -1)
return false;
return ($this->cursorPos > $this->lastPos);
}
}

View File

@ -0,0 +1,362 @@
<?php
/*
* $Id: ODBCConnection.php,v 1.6 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Connection.php';
require_once 'creole/common/ConnectionCommon.php';
require_once 'creole/drivers/odbc/adapters/ODBCAdapter.php';
/**
* ODBC implementation of Connection.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.6 $
* @package creole.drivers.odbc
*/
class ODBCConnection extends ConnectionCommon implements Connection {
/**
* Implements driver-specific behavior
* @var ODBCAdapter
*/
protected $adapter = null;
/**
* Last ODBC result resource from executeQuery/executeUpdate. Used in getUpdateCount()
* @var ODBCResultResource
*/
protected $odbcresult = null;
/**
* @see Connection::connect()
*/
public function connect($dsninfo, $flags = 0)
{
if (!function_exists('odbc_connect'))
throw new SQLException('odbc extension not loaded');
$adapterclass = isset($dsninfo['adapter']) ? $dsninfo['adapter'] : null;
if (!$adapterclass)
$adapterclass = 'ODBCAdapter';
else
$adapterclass .= 'Adapter';
Creole::import('creole.drivers.odbc.adapters.' . $adapterclass);
$this->adapter = new $adapterclass();
$this->dsn = $dsninfo;
$this->flags = $flags;
if ( !($this->flags & Creole::COMPAT_ASSOC_LOWER) && !$this->adapter->preservesColumnCase())
{
trigger_error('Connection created without Creole::COMPAT_ASSOC_LOWER, ' .
'but driver does not support case preservation.',
E_USER_WARNING);
$this->flags != Creole::COMPAT_ASSOC_LOWER;
}
$persistent = ($flags & Creole::PERSISTENT) === Creole::PERSISTENT;
if ($dsninfo['database'])
$odbcdsn = $dsninfo['database'];
elseif ($dsninfo['hostspec'])
$odbcdsn = $dsninfo['hostspec'];
else
$odbcdsn = 'localhost';
$user = @$dsninfo['username'];
$pw = @$dsninfo['password'];
$connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect';
$conn = @$connect_function($odbcdsn, $user, $pw, SQL_CUR_USE_IF_NEEDED);
if (!is_resource($conn))
throw new SQLException('connect failed', $this->nativeError(), $odbcdsn);
$this->dblink = $conn;
/**
* This prevents blob fields from being fetched when a row is loaded
* from a recordset. Clob fields however are loaded with up to
* 'odbc.defaultlrl' data. This should be the default anyway, but we'll
* set it here just to keep things consistent.
*/
@odbc_binmode(0, ODBC_BINMODE_PASSTHRU);
@odbc_longreadlen(0, ini_get('odbc.defaultlrl'));
}
/**
* @see Connection::close()
*/
public function close()
{
$ret = true;
$this->adapter = null;
$this->odbcresult = null;
if ($this->dblink !== null)
{
$ret = @odbc_close($this->dblink);
$this->dblink = null;
}
return $ret;
}
/**
* Shouldn't this be in ConnectionCommon.php?
*/
public function __destruct()
{
$this->close();
}
/**
* Returns a formatted ODBC error string.
* @return string
*/
public function nativeError()
{
if ($this->dblink && is_resource($this->dblink))
$errstr = '[' . @odbc_error($this->dblink) . '] ' . @odbc_errormsg($this->dblink);
else
$errstr = '[' . @odbc_error() . '] ' . @odbc_errormsg();
return $errstr;
}
/**
* Returns driver-specific ODBCAdapter.
* @return ODBCAdapter
*/
public function getAdapter()
{
return $this->adapter;
}
/**
* @see Connection::getDatabaseInfo()
*/
public function getDatabaseInfo()
{
require_once 'creole/drivers/odbc/metadata/ODBCDatabaseInfo.php';
return new ODBCDatabaseInfo($this);
}
/**
* @see Connection::getIdGenerator()
*/
public function getIdGenerator()
{
return $this->adapter->getIdGenerator($this);
}
/**
* Creates the appropriate ResultSet
* @return ResultSet
*/
public function createResultSet($odbcresult, $fetchmode)
{
return $this->adapter->createResultSet($this, $odbcresult, $fetchmode);
}
/**
* @see Connection::prepareStatement()
*/
public function prepareStatement($sql)
{
require_once 'creole/drivers/odbc/ODBCPreparedStatement.php';
return new ODBCPreparedStatement($this, $sql);
}
/**
* @see Connection::createStatement()
*/
public function createStatement()
{
require_once 'creole/drivers/odbc/ODBCStatement.php';
return new ODBCStatement($this);
}
/**
* @todo To be implemented
* @see Connection::prepareCall()
*/
public function prepareCall($sql)
{
throw new SQLException('Stored procedures not currently implemented.');
}
/**
* @see Connection::applyLimit()
*/
public function applyLimit(&$sql, $offset, $limit)
{
if ($this->adapter->hasLimitOffset())
$this->adapter->applyLimit($sql, $offset, $limit);
}
/**
* @see Connection::executeQuery()
*/
public function executeQuery($sql, $fetchmode = null)
{
if ($this->odbcresult)
$this->odbcresult = null;
$r = @odbc_exec($this->dblink, $sql);
if ($r === false)
throw new SQLException('Could not execute query', $this->nativeError(), $sql);
$this->odbcresult = new ODBCResultResource($r);
return $this->createResultSet($this->odbcresult, $fetchmode);
}
/**
* @see Connection::executeUpdate()
*/
public function executeUpdate($sql)
{
if ($this->odbcresult)
$this->odbcresult = null;
$r = @odbc_exec($this->dblink, $sql);
if ($r === false)
throw new SQLException('Could not execute update', $this->nativeError(), $sql);
$this->odbcresult = new ODBCResultResource($r);
return $this->getUpdateCount();
}
/**
* Start a database transaction.
* @throws SQLException
* @return void
*/
protected function beginTrans()
{
if ($this->adapter->supportsTransactions()) {
@odbc_autocommit($this->dblink, false);
if (odbc_error($this->dblink) == 'S1C00') {
throw new SQLException('Could not begin transaction', $this->nativeError());
}
}
}
/**
* Commit the current transaction.
* @throws SQLException
* @return void
*/
protected function commitTrans()
{
if ($this->adapter->supportsTransactions()) {
$result = @odbc_commit($this->dblink);
if (!$result) {
throw new SQLException('Could not commit transaction', $this->nativeError());
}
@odbc_autocommit($this->dblink, true);
if (odbc_error($this->dblink) == 'S1C00') {
throw new SQLException('Could not commit transaction (autocommit failed)', $this->nativeError());
}
}
}
/**
* Roll back (undo) the current transaction.
* @throws SQLException
* @return void
*/
protected function rollbackTrans()
{
if ($this->adapter->supportsTransactions()) {
$result = @odbc_rollback($this->dblink);
if (!$result) {
throw new SQLException('Could not rollback transaction', $this->nativeError());
}
@odbc_autocommit($this->dblink, true);
if (odbc_error($this->dblink) == 'S1C00') {
throw new SQLException('Could not rollback transaction (autocommit failed)', $this->nativeError());
}
}
}
/**
* @see Connection::getUpdateCount()
*/
public function getUpdateCount()
{
if ($this->odbcresult === null)
return 0;
$n = @odbc_num_rows($this->odbcresult->getHandle());
if ($n == -1)
throw new SQLException('Could not retrieve update count', $this->nativeError());
return (int) $n;
}
}
/**
* This is a simple wrapper class to manage the lifetime of an ODBC result resource
* (returned by odbc_exec(), odbc_execute(), etc.) We use a separate class because
* the resource can be shared by both ODBCConnection and an ODBCResultSet at the
* same time. ODBCConnection hangs on to the last result resource to be used in
* its getUpdateCount() method. It also passes this resource to new instances of
* ODBCResultSet. At some point the resource has to be cleaned up via
* odbc_free_result(). Using this class as a wrapper, we can pass around multiple
* references to the same resource. PHP's reference counting mechanism will clean
* up the resource when its no longer used via ODBCResultResource::__destruct().
* @package creole.drivers.odbc
*/
class ODBCResultResource
{
/**
* @var resource ODBC result resource returned by {@link odbc_exec()}/{@link odbc_execute()}.
*/
protected $handle = null;
public function __construct($handle)
{
if (is_resource($handle))
$this->handle = $handle;
}
public function __destruct()
{
if ($this->handle !== null)
@odbc_free_result($this->handle);
}
public function getHandle()
{
return $this->handle;
}
}

View File

@ -0,0 +1,118 @@
<?php
require_once 'creole/IdGenerator.php';
/**
* ODBC IdGenerator implimenation.
*
* NOTE: I tried keeping the SQL as basic as possible in this class.
* If you need something more optimized, derive your own IdGenerator
* and use {@link ODBCAdapter::getIdGenerator()} to use it.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.2 $
* @package creole.drivers.odbc
*/
class ODBCIdGenerator implements IdGenerator {
/** Connection object that instantiated this class */
private $conn;
/**
* Creates a new IdGenerator class, saves passed connection for use
* later by getId() method.
* @param Connection $conn
*/
public function __construct(Connection $conn)
{
$this->conn = $conn;
}
/**
* @see IdGenerator::isBeforeInsert()
*/
public function isBeforeInsert()
{
return true;
}
/**
* @see IdGenerator::isAfterInsert()
*/
public function isAfterInsert()
{
return false;
}
/**
* @see IdGenerator::getIdMethod()
*/
public function getIdMethod()
{
return self::SEQUENCE;
}
/**
* @see IdGenerator::getId()
*/
public function getId($seqname = null)
{
if ($seqname === null)
throw new SQLException('You must specify the sequence name when calling getId() method.');
$triedcreate = false;
while (1)
{
try
{
$n = $this->conn->executeUpdate("UPDATE $seqname SET id = id + 1", ResultSet::FETCHMODE_NUM);
if ($n == 0)
throw new SQLException('Failed to update IdGenerator id', $this->conn->nativeError());
$rs = $this->conn->executeQuery("SELECT id FROM $seqname", ResultSet::FETCHMODE_NUM);
}
catch (SQLException $e)
{
//$odbcerr = odbc_error($this->conn->getResource());
if ($triedcreate)// || ($odbcerr != 'S0000' && $odbcerr != 'S0002'))
throw $e;
$this->drop($seqname, true);
$this->create($seqname);
$triedcreate = true;
continue;
}
break;
}
$rs->first();
return $rs->getInt(1);
}
/**
* Creates the sequence emulation table.
*/
public function create($seqname)
{
$this->conn->executeUpdate("CREATE TABLE $seqname ( id numeric(19,0) NOT NULL )");
$this->conn->executeUpdate("INSERT INTO $seqname ( id ) VALUES ( 0 )");
}
/**
* Drops the sequence emulation table.
*/
public function drop($seqname, $ignoreerrs = false)
{
try {
$this->conn->executeUpdate("DROP TABLE $seqname");
} catch (Exception $e) {
if (!$ignoreerrs) throw $e;
}
}
}

View File

@ -0,0 +1,246 @@
<?php
/*
* $Id: ODBCPreparedStatement.php,v 1.4 2005/11/13 01:29:01 gamr Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/PreparedStatement.php';
require_once 'creole/common/PreparedStatementCommon.php';
require_once 'creole/util/Lob.php';
/**
* ODBC specific PreparedStatement functions.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.4 $
* @package creole.drivers.odbc
*/
class ODBCPreparedStatement extends PreparedStatementCommon implements PreparedStatement
{
/**
* This does nothing since ODBC natively supports prepared statements.
* @see PreparedStatementCommon::replaceParams()
*/
protected function replaceParams()
{
if ($this->conn->getAdapter()->emulatePrepareStmt())
return parent::replaceParams();
else
return $this->sql;
}
/**
* Internal function to call native ODBC prepare/execute functions.
*/
protected function _execute($sql, $params, $fetchmode, $isupdate)
{
if ($this->resultSet)
{
$this->resultSet->close();
$this->resultSet = null;
}
$this->updateCount = null;
if ($this->conn->getAdapter()->emulatePrepareStmt())
{
$stmt = @odbc_exec($this->conn->getResource(), $sql);
$ret = ($stmt !== false);
}
else
{
// Trim surrounding quotes added from default set methods.
// Exception: for LOB-based parameters, odbc_execute() will
// accept a filename surrounded by single-quotes.
foreach ($this->boundInVars as $idx => $var)
{
if ($var instanceof Lob)
{
$file = ($isupdate ? $var->getInputFile() : $var->getOutputFile());
$this->boundInVars[$idx] = "'$file'";
}
else if (is_string($var))
{
$this->boundInVars[$idx] = trim($var, "\"\'");
}
}
$stmt = @odbc_prepare($this->conn->getResource(), $sql);
if ($stmt === FALSE)
throw new SQLException('Could not prepare query', $this->conn->nativeError(), $sql);
$ret = @odbc_execute($stmt, $this->boundInVars);
}
if ($ret === FALSE)
{
@odbc_free_result($stmt);
throw new SQLException('Could not execute query', $this->conn->nativeError(), $sql);
}
return $this->conn->createResultSet(new ODBCResultResource($stmt), $fetchmode);
}
/**
* @see PreparedStatement::executeQuery()
*/
public function executeQuery()
{
switch (func_num_args()) {
case 2:
list($params, $fetchmode) = func_get_args();
if (!is_array($params)) {
unset($params);
}
break;
case 1:
$params = null;
list($fetchmode) = func_get_args();
break;
case 0:
$params = null;
$fetchmode = null;
break;
}
// Set any params passed directly
if (isset($params)) {
for($i=0,$cnt=count($params); $i < $cnt; $i++) {
$this->set($i+1, $params[$i]);
}
}
$sql = $this->replaceParams();
if ($this->conn->getAdapter()->hasLimitOffset())
{
if ($this->limit > 0 || $this->offset > 0)
$this->conn->applyLimit($sql, $this->offset, $this->limit);
}
$this->resultSet = $this->_execute($sql, $params, $fetchmode, false);
if (!$this->conn->getAdapter()->hasLimitOffset())
{
$this->resultSet->_setOffset($this->offset);
$this->resultSet->_setLimit($this->limit);
}
return $this->resultSet;
}
/**
* @see PreparedStatement::executeUpdate()
*/
public function executeUpdate($params = null)
{
// Set any params passed directly
if ($params) {
for($i=0,$cnt=count($params); $i < $cnt; $i++) {
$this->set($i+1, $params[$i]);
}
}
$sql = $this->replaceParams();
$this->_execute($sql, $params, 0, true);
$this->updateCount = $this->conn->getUpdateCount();
return $this->updateCount;
}
/**
* @see PreparedStatementCommon::escape()
*/
protected function escape($str)
{
if ($this->conn->getAdapter()->emulatePrepareStmt())
return $this->conn->getAdapter()->escape($str);
// Nothing to do here. odbc_execute() takes care of escaping strings.
return $str;
}
/**
* @see PreparedStatement::setNull()
*/
function setNull($paramIndex)
{
$this->sql_cache_valid = false;
$this->boundInVars[$paramIndex] = null;
}
/**
* @see PreparedStatement::setBlob()
*/
function setBlob($paramIndex, $blob)
{
if ($this->conn->getAdapter()->emulatePrepareStmt())
return parent::setBlob($paramIndex, $blob);
$this->sql_cache_valid = false;
if ($blob === null)
{
$this->setNull($paramIndex);
return;
}
if ($blob instanceof Blob)
{
if ($blob->isFromFile() && !$blob->isModified())
{
$this->boundInVars[$paramIndex] = $blob;
return;
}
$blob = $blob->__toString();
}
$this->boundInVars[$paramIndex] = "'" . $this->escape($blob) . "'";
}
/**
* @see PreparedStatement::setClob()
*/
function setClob($paramIndex, $clob)
{
if ($this->conn->getAdapter()->emulatePrepareStmt())
return parent::setClob($paramIndex, $clob);
$this->sql_cache_valid = false;
if ($clob === null)
{
$this->setNull($paramIndex);
return;
}
if ($clob instanceof Clob)
{
if ($clob->isFromFile() && !$clob->isModified())
{
$this->boundInVars[$paramIndex] = $clob;
return;
}
$clob = $clob->__toString();
}
$this->boundInVars[$paramIndex] = "'" . $this->escape($clob) . "'";
}
}

View File

@ -0,0 +1,209 @@
<?php
/*
* $Id: ODBCResultSet.php,v 1.2 2005/04/01 17:10:42 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/drivers/odbc/ODBCResultSetCommon.php';
/**
* ODBC implementation of ResultSet.
*
* If the current ODBC driver does not support LIMIT or OFFSET natively,
* the methods in here perform some adjustments and extra checking to make
* sure that this behaves the same as RDBMS drivers using native OFFSET/LIMIT.
*
* This class also emulates a row count if the driver is not capable of
* providing one natively.
*
* NOTE: This class only works with drivers that support absolute cursor
* positioning (SQL_FETCH_DIRECTION = SQL_FD_FETCH_ABSOLUTE). If the
* driver you are using does not support reverse/absolute cursor
* scrolling, you should use the {@link ODBCCachedResultSet} class instead.
* See the documentation for ODBCCachedResultSet for instructions on how
* to use it.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.2 $
* @package creole.drivers.odbc
*/
class ODBCResultSet extends ODBCResultSetCommon implements ResultSet
{
/**
* Number of rows in resultset.
*
* @var int
*/
protected $numRows = -1;
/**
* True if ODBC driver supports odbc_num_rows().
*
* @var boolean
*/
protected $hasRowCount = false;
/**
* @see ResultSet::__construct()
*/
public function __construct(Connection $conn, $result, $fetchmode = null)
{
parent::__construct($conn, $result, $fetchmode);
/**
* Some ODBC drivers appear not to handle odbc_num_rows() very well when
* more than one result handle is active at once. For example, the MySQL
* ODBC driver always returns the number of rows for the last executed
* result. For this reason, we'll store the row count here.
*
* Note also that many ODBC drivers do not support this method. In this
* case, getRecordCount() will perform a manual count.
*/
$this->numRows = @odbc_num_rows($result->getHandle());
$this->hasRowCount = $this->numRows != -1;
}
/**
* @see ODBCResultSetCommon::close()
*/
function close()
{
parent::close();
$numRows = -1;
}
/**
* @see ResultSet::seek()
*/
public function seek($rownum)
{
if ($rownum < 0 || $this->limit > 0 && $rownum > $this->limit)
return false;
$this->cursorPos = $rownum;
return true;
}
/**
* @see ResultSet::next()
*/
public function next()
{
$this->cursorPos++;
if ($this->limit > 0 && $this->cursorPos > $this->limit) {
$this->cursorPos = $this->limit+1;
return false;
}
$rowNum = $this->offset + $this->cursorPos;
$fields = null;
$cols = @odbc_fetch_into($this->result->getHandle(), $fields, $rowNum);
if ($cols === false) {
$this->cursorPos = -1;
return false;
}
$this->fields =& $this->checkFetchMode($fields);
return true;
}
/**
* @see ResultSet::isAfterLast()
*/
public function isAfterLast()
{
// Force calculation of last record pos.
if ($this->cursorPos == -1)
$this->getRecordCount();
return parent::isAfterLast();
}
/**
* @see ResultSet::getRecordCount()
*/
function getRecordCount()
{
if ($this->hasRowCount)
{
// Use driver row count if provided.
$numRows = $this->numRows - $this->offset;
if ($this->limit > 0 && $numRows > $this->limit)
$numRows = $this->limit;
}
else
{
// Do manual row count if driver doesn't provide one.
if ($this->numRows == -1)
{
$this->numRows = 0;
$this->beforeFirst();
while($this->next())
$this->numRows++;
}
$numRows = $this->numRows;
}
// Cursor pos is -1 when an attempt to fetch past the last row was made
// (or a fetch error occured).
if ($this->cursorPos == -1)
$this->cursorPos = $numRows+1;
return $numRows;
}
/**
* @see ResultSet::getBlob()
*/
public function getBlob($column)
{
require_once 'creole/util/Blob.php';
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
$data = $this->readLobData($column, ODBC_BINMODE_RETURN, $this->fields[$idx]);
if (!$data) { return null; }
$b = new Blob();
$b->setContents($data);
return $b;
}
/**
* @see ResultSet::getClob()
*/
public function getClob($column)
{
require_once 'creole/util/Clob.php';
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
$data = $this->readLobData($column, ODBC_BINMODE_CONVERT, $this->fields[$idx]);
if (!$data) { return null; }
$c = new Clob();
$c->setContents($data);
return $c;
}
}

View File

@ -0,0 +1,188 @@
<?php
/*
* $Id: ODBCResultSetCommon.php,v 1.3 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/ResultSet.php';
require_once 'creole/common/ResultSetCommon.php';
/**
* Base class for ODBC implementation of ResultSet.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.3 $
* @package creole.drivers.odbc
*/
abstract class ODBCResultSetCommon extends ResultSetCommon
{
/**
* Offset at which to start reading rows (for emulated offset).
* @var int
*/
protected $offset = 0;
/**
* Maximum rows to retrieve, or 0 if all (for emulated limit).
* @var int
*/
protected $limit = 0;
/**
* @see ResultSet::__construct()
*/
public function __construct(Connection $conn, $result, $fetchmode = null)
{
parent::__construct($conn, $result, $fetchmode);
}
/**
* @see ResultSet::close()
*/
public function close()
{
$this->result = null;
$this->conn = null;
$this->fetchmode = null;
$this->cursorPos = 0;
$this->fields = null;
$this->lowerAssocCase = false;
$this->limit = 0;
$this->offset = 0;
}
/**
* This function exists to set offset after ResultSet is instantiated.
* This function should be "protected" in Java sense: only available to classes in package.
* THIS METHOD SHOULD NOT BE CALLED BY ANYTHING EXCEPTION DRIVER CLASSES.
* @param int $offset New offset.
* @access protected
*/
public function _setOffset($offset)
{
$this->offset = $offset;
}
/**
* This function exists to set limit after ResultSet is instantiated.
* This function should be "protected" in Java sense: only available to classes in package.
* THIS METHOD SHOULD NOT BE CALLED BY ANYTHING EXCEPTION DRIVER CLASSES.
* @param int $limit New limit.
* @access protected
*/
public function _setLimit($limit)
{
$this->limit = $limit;
}
/**
* If fetchmode is FETCHMODE_ASSOC, returns the 1-based field index number
* for the specified column name. Otherwise returns 0 (false).
* @return int
*/
function getFieldNum($colname)
{
$fieldnum = 0;
if ($this->fetchmode == ResultSet::FETCHMODE_ASSOC)
{
$keys = array_keys($this->fields);
$fieldnum = array_search($colname, $keys);
}
return $fieldnum;
}
/**
* Reads in any unread LOB data. For long char fields, we may already
* have up to odbc_longreadlen() bytes in the buffer. These are passed
* in via the $curdata parm. For long binary fields, no data is read
* initially since odbc_binmode() is set to ODBC_BINMODE_PASSTHRU.
* This method adjusts the binmode and longreadlen to finish reading
* these datatypes into the buffer. Returns a string with the complete
* contents.
*
* @param int|string $column Column index or name to read data from.
* @param int $binmode ODBC_BINMODE_RETURN for binary data, ODBC_BINMODE_CONVERT for char data.
* @param string $curdata Existing LOB data already in buffer.
* @return string
*/
protected function readLobData($column, $binmode, $curdata = null)
{
// Retrieve field num
$fldNum = (is_int($column) ? $column : getFieldNum($column));
$data = $curdata;
$newdata = null;
// Adjust binmode and longreadlen
odbc_binmode($this->result->getHandle(), $binmode);
odbc_longreadlen($this->result->getHandle(), 4096);
while (1)
{
$newdata = odbc_result($this->result->getHandle(), $fldNum);
if ($newdata === false)
break;
else
$data .= $newdata;
}
// Restore the default binmode and longreadlen
odbc_binmode($this->result->getHandle(), ODBC_BINMODE_PASSTHRU);
odbc_longreadlen($this->result->getHandle(), ini_get('odbc.defaultlrl'));
// The ODBC driver I use seems to return a string with an escaped
// null char at the end for clob data.
$data = rtrim($data, "\x0");
return $data;
}
/**
* Converts row fields to names if FETCHMODE_ASSOC is set.
*
* @param array& Row to convert.
*
* @return array& Converted row.
*/
protected function checkFetchMode(&$row)
{
if ($this->fetchmode == ResultSet::FETCHMODE_ASSOC)
{
$newrow = array();
for ($i = 0, $n = count($row); $i < $n; $i++)
{
$colname = @odbc_field_name($this->result->getHandle(), $i+1);
if ($this->lowerAssocCase) {
$colname = strtolower($colname);
}
$newrow[$colname] = $row[$i];
}
$row =& $newrow;
}
return $row;
}
}

View File

@ -0,0 +1,64 @@
<?php
/*
* $Id: ODBCStatement.php,v 1.1 2004/07/27 23:08:30 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Statement.php';
require_once 'creole/common/StatementCommon.php';
/**
* ODBC Statement
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.1 $
* @package creole.drivers.odbc
*/
class ODBCStatement extends StatementCommon implements Statement
{
/**
* @see Statement::executeQuery()
*/
public function executeQuery($sql, $fetchmode = null)
{
if ($this->resultSet)
{
$this->resultSet->close();
$this->resultSet = null;
}
$this->updateCount = null;
if ($this->conn->getAdapter()->hasLimitOffset())
{
if ($this->limit > 0 || $this->offset > 0)
$this->conn->applyLimit($sql, $this->offset, $this->limit);
}
$this->resultSet = $this->conn->executeQuery($sql, $fetchmode);
if (!$this->conn->getAdapter()->hasLimitOffset())
{
$this->resultSet->_setOffset($this->offset);
$this->resultSet->_setLimit($this->limit);
}
return $this->resultSet;
}
}

View File

@ -0,0 +1,189 @@
<?php
/*
* $Id: ODBCTypes.php,v 1.1 2004/07/27 23:08:30 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/CreoleTypes.php';
/**
* ODBC types / type map.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.1 $
* @package creole.drivers.odbc
*/
class ODBCTypes extends CreoleTypes {
/**
* Map ODBC native types to Creole (JDBC) types.
*/
protected static $typeMap = null;
/**
* Reverse mapping, created on demand.
*/
protected static $reverseMap = null;
/**
* Loads the map of ODBC data types to Creole (JDBC) types.
*
* NOTE: This function cannot map DBMS-specific datatypes. If you use a
* driver which implements DBMS-specific datatypes, you will need
* to modify/extend this class to add the correct mapping.
*/
public static function loadTypeMap($conn = null)
{
if (self::$typeMap !== null && count(self::$typeMap) > 0)
return;
if ($conn == null)
throw new SQLException('No connection specified when loading ODBC type map.');
self::$typeMap = array();
$result = @odbc_gettypeinfo($conn->getResource());
if ($result === false)
throw new SQLException('Failed to retrieve type info.', $conn->nativeError());
$rowNum = 1;
while (odbc_fetch_row($result, $rowNum++))
{
$odbctypeid = odbc_result($result, 'DATA_TYPE');
$odbctypename = odbc_result($result, 'TYPE_NAME');
switch ($odbctypeid)
{
case SQL_CHAR:
self::$typeMap[$odbctypename] = CreoleTypes::CHAR;
break;
case SQL_VARCHAR:
self::$typeMap[$odbctypename] = CreoleTypes::VARCHAR;
break;
case SQL_LONGVARCHAR:
self::$typeMap[$odbctypename] = CreoleTypes::LONGVARCHAR;
break;
case SQL_DECIMAL:
self::$typeMap[$odbctypename] = CreoleTypes::DECIMAL;
break;
case SQL_NUMERIC:
self::$typeMap[$odbctypename] = CreoleTypes::NUMERIC;
break;
case SQL_BIT:
self::$typeMap[$odbctypename] = CreoleTypes::BOOLEAN;
break;
case SQL_TINYINT:
self::$typeMap[$odbctypename] = CreoleTypes::TINYINT;
break;
case SQL_SMALLINT:
self::$typeMap[$odbctypename] = CreoleTypes::SMALLINT;
break;
case SQL_INTEGER:
self::$typeMap[$odbctypename] = CreoleTypes::INTEGER;
break;
case SQL_BIGINT:
self::$typeMap[$odbctypename] = CreoleTypes::BIGINT;
break;
case SQL_REAL:
self::$typeMap[$odbctypename] = CreoleTypes::REAL;
break;
case SQL_FLOAT:
self::$typeMap[$odbctypename] = CreoleTypes::FLOAT;
break;
case SQL_DOUBLE:
self::$typeMap[$odbctypename] = CreoleTypes::DOUBLE;
break;
case SQL_BINARY:
self::$typeMap[$odbctypename] = CreoleTypes::BINARY;
break;
case SQL_VARBINARY:
self::$typeMap[$odbctypename] = CreoleTypes::VARBINARY;
break;
case SQL_LONGVARBINARY:
self::$typeMap[$odbctypename] = CreoleTypes::LONGVARBINARY;
break;
case SQL_DATE:
self::$typeMap[$odbctypename] = CreoleTypes::DATE;
break;
case SQL_TIME:
self::$typeMap[$odbctypename] = CreoleTypes::TIME;
break;
case SQL_TIMESTAMP:
self::$typeMap[$odbctypename] = CreoleTypes::TIMESTAMP;
break;
case SQL_TYPE_DATE:
self::$typeMap[$odbctypename] = CreoleTypes::DATE;
break;
case SQL_TYPE_TIME:
self::$typeMap[$odbctypename] = CreoleTypes::TIME;
break;
case SQL_TYPE_TIMESTAMP:
self::$typeMap[$odbctypename] = CreoleTypes::TIMESTAMP;
break;
default:
self::$typeMap[$odbctypename] = CreoleTypes::OTHER;
break;
}
}
@odbc_free_result($result);
}
/**
* This method returns the generic Creole (JDBC-like) type
* when given the native db type.
* @param string $nativeType DB native type (e.g. 'TEXT', 'byetea', etc.).
* @return int Creole native type (e.g. CreoleTypes::LONGVARCHAR, CreoleTypes::BINARY, etc.).
*/
public static function getType($nativeType)
{
if (!self::$typeMap)
self::loadTypeMap();
$t = strtoupper($nativeType);
if (isset(self::$typeMap[$t])) {
return self::$typeMap[$t];
} else {
return CreoleTypes::OTHER;
}
}
/**
* This method will return a native type that corresponds to the specified
* Creole (JDBC-like) type.
* If there is more than one matching native type, then the LAST defined
* native type will be returned.
* @param int $creoleType
* @return string Native type string.
*/
public static function getNativeType($creoleType)
{
if (!self::$typeMap)
self::loadTypeMap();
if (self::$reverseMap === null) {
self::$reverseMap = array_flip(self::$typeMap);
}
return @self::$reverseMap[$creoleType];
}
}

90
lib/symfony/vendor/creole/drivers/odbc/README vendored Executable file
View File

@ -0,0 +1,90 @@
Creole ODBC Bridge Driver
=========================
I. Overview
-----------
In the text below, the word "driver" can get somewhat muddled since there are
two libraries concerned here (Creole & ODBC). So, we'll use the term "bridge
driver" to refer to Creole's ODBC bridge driver, and "ODBC driver" to refer to
an ODBC database driver.
The Creole ODBC Bridge driver provides a solution for databases which
currently have no PHP-native interface. It is currently in an experimental
stage of development. It has been tested with two ODBC drivers (Sequiter's
CodeBase ODBC driver and the MySQL ODBC driver (as a baseline test)). To
use any other ODBC drivers you may need to write your own ODBCAdapter-derived
class (see below).
II. ODBCAdapter
---------------
Because ODBC itself is a database abstraction library, the bridge driver needed
a way of hiding ODBC driver-specific behavior. The solution to this was to
create an adapter layer (akin to how the Propel runtime engine works). Think of
it as a sub-driver for the bridge driver. Any ODBC driver-specific behavior is
handled by an ODBCAdapter-derived class. To use a specific adapter class, you
specify its name via a parameter in the connection string:
odbc://localhost/DSN=CodeBase;?adapter=CodeBase
The string above will load the following file as the adapter to use with the
bridge driver: creole/drivers/odbc/adapters/CodeBaseAdapter.php
Some ODBC drivers are limited in support for various Creole features. The
ODBCAdapter also provides a method for emulation of some of these missing
features:
-The emulatePrepareStmt() method provides a switch for enabling prepared
statement emulation for drivers that do not support (or have trouble with)
prepared statements. This emulation is disabled by default.
-The hasLimitOffset() method provides a switch for enabling LIMIT/OFFSET
emulation for drivers that do not support this. This emulation is enabled
by default. The LIMIT/OFFSET emulation was borrowed from the MSSQL Creole
driver.
-The createResultSet() method provides a switch for enabling cached
result sets. To enable this feature, return an instance of
ODBCCachedResultSet in the createResultSet() method of your ODBCAdapter-
derived class. This can be useful as a workaround for ODBC drivers which
lack support for record count retrieval, reverse/absolute cursor
scrolling, etc. In most cases, result rows are cached on-demand. So if
you only read the first couple rows of a result, then only those rows will
be cached.
-The getIdGenerator() method provides a switch for enabling sequence
emulation. This feature is enabled by default in ODBCAdapter and is
implemented in the ODBCIdGenerator class. The emulation code was inspired
by the PEAR::DB nextID() method. If your database supports sequences or
autoincrement natively, you can return your own IdGenerator-derived class
instead. Check out some of the other Creole drivers for IdGenerator
examples.
III. Incomplete Features
------------------------
-The database metadata classes are not fully tested/complete. Specifically,
the ODBCDatabaseInfo class does not currently set the database name. There
may be other problems as well.
-The Creole CallableStatement class (stored procedures) is not currently
implemented. No immediate plans to do this in the future, but it looks
feasible.
IV. Known Issues
----------------
This driver was developed using the PHP v5.0 final build. During the course
of testing I uncovered several bugs in the php_odbc module. I submitted
patches for these bugs, but have not yet received word that they were
committed (they were just submitted this morning). If you want more details
on the problems I encountered or would like a copy of the patches, please
e-mail me (dlawson@masterytech.com).

View File

@ -0,0 +1,73 @@
<?php
/*
* $Id: CodeBaseAdapter.php,v 1.3 2005/10/17 19:03:51 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/drivers/odbc/adapters/ODBCAdapter.php';
/**
* CodeBase driver-specific behavior.
*
* This adapter is for Sequiter's CodeBaseSQL product. It is a dBase ODBC
* driver. The driver only supports forward-only cursor scrolling so this
* adapter causes the ODBCCachedResultSet to be used.
*
* A couple other quirks exist:
*
* 1) Cannot get blobs to work correctly. If I try writing one to a
* LONGVARBINARY typed field, only the first few bytes are written.
* This will cause the ResultSetTest::testGetBlob() test case to fail
* when running tests for the driver.
*
* 2) For some reason the character count is off for the
* ResultSetTest::testSetClob() test case _only_ when running from the
* command line. If I run the same test through a web server it works fine.
* Looks like it has something to do with line endings in Windows. The
* difference in file sizes is 9803 vs 10090.
*
* 3) Setting a clob field to null writes a space to the field in the table.
* This causes the PreparedStatementTest::testSetNull() test case to fail
* when running tests for the driver.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.3 $
* @package creole.drivers.odbc
*/
class CodeBaseAdapter extends ODBCAdapter
{
/**
* @see ODBCAdapter::createResultSet()
*/
public function preservesColumnCase()
{
return false;
}
/**
* @see ODBCAdapter::createResultSet()
*/
public function createResultSet($conn, $odbcresult, $fetchmode)
{
require_once 'creole/drivers/odbc/ODBCResultSet.php';
return new ODBCResultSet($conn, $odbcresult, $fetchmode, true);
}
}
?>

View File

@ -0,0 +1,78 @@
<?php
/*
* $Id: MySQLAdapter.php,v 1.1 2004/07/27 23:08:30 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/drivers/odbc/ODBCCachedResultSet.php';
require_once 'creole/drivers/odbc/ODBCResultSet.php';
require_once 'creole/drivers/odbc/adapters/ODBCAdapter.php';
/**
* Implements MySQL driver-specific behavior.
*
* Obviously it would be much more efficient to simply use the Creole
* MySQL driver. This adapter was created for the sole purpose of testing
* the ODBC driver.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.1 $
* @package creole.drivers.odbc
*/
class MySQLAdapter extends ODBCAdapter
{
/**
* @see ODBCAdapter::hasLimitOffset()
*/
public function hasLimitOffset()
{
return true;
}
/**
* @see ODBCAdapter::applyLimit()
*/
public function applyLimit(&$sql, $offset, $limit)
{
if ( $limit > 0 ) {
$sql .= " LIMIT " . ($offset > 0 ? $offset . ", " : "") . $limit;
} else if ( $offset > 0 ) {
$sql .= " LIMIT " . $offset . ", 18446744073709551615";
}
}
/**
* @see ODBCAdapter::escape()
*/
public function escape($str)
{
return addslashes($str);
}
/**
* @see ODBCAdapter::createResultSet()
*/
public function createResultSet($conn, $odbcresult, $fetchmode)
{
// return new ODBCCachedResultSet($conn, $odbcresult, $fetchmode, true);
return new ODBCResultSet($conn, $odbcresult, $fetchmode);
}
}
?>

View File

@ -0,0 +1,115 @@
<?php
/*
* $Id: ODBCAdapter.php,v 1.3 2005/10/17 19:03:51 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Default class for ODBC driver-specific behavior.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.3 $
* @package creole.drivers.odbc
*/
class ODBCAdapter
{
/**
* Returns true if column case is preserved in the database when a table
* is first created. Returns false if table does not preserve case (i.e.
* ProductID => PRODUCTID).
*
* @return boolean
*/
public function preservesColumnCase()
{
return true;
}
/**
* Returns true if prepared statements should be emulated. This
* might be useful if your driver does not support (or has trouble with)
* prepared statements.
*
* @return boolean
*/
public function emulatePrepareStmt()
{
return false;
}
/**
* Returns true if ODBC driver supports LIMIT/OFFSET via SQL.
*
* @return boolean
*/
public function hasLimitOffset()
{
return false;
}
/**
* @see Connection::applyLimit()
*/
public function applyLimit(&$sql, $offset, $limit)
{
}
/**
* @see PreparedStatementCommon::escape()
*/
public function escape($str)
{
// use this instead of magic_quotes_sybase + addslashes(),
// just in case multiple RDBMS being used at the same time
return str_replace("'", "''", $str);
}
/**
* Returns an instance of the default resultset.
*
* @return boolean
*/
public function createResultSet($conn, $odbcresult, $fetchmode)
{
require_once 'creole/drivers/odbc/ODBCResultSet.php';
return new ODBCResultSet($conn, $odbcresult, $fetchmode);
}
/**
* Returns the default ODBCIdGenerator for emulating sequences.
*
* @return ODBCIdGenerator
*/
public function getIdGenerator($conn)
{
require_once 'creole/drivers/odbc/ODBCIdGenerator.php';
return new ODBCIdGenerator($conn);
}
/**
* Returns true if driver support transactions.
*
* @return boolean
*/
public function supportsTransactions()
{
return true;
}
}
?>

View File

@ -0,0 +1,66 @@
<?php
/*
* $Id: ODBCDatabaseInfo.php,v 1.2 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/DatabaseInfo.php';
/**
* ODBC implementation of DatabaseInfo.
*
* @todo Still need a way to obtain the database name. Not sure how to do this yet.
* @todo This might need to be an {@link ODBCAdapter} method.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.2 $
* @package creole.drivers.odbc.metadata
*/
class ODBCDatabaseInfo extends DatabaseInfo {
/**
* @see DatabaseInfo::initTables()
*/
protected function initTables()
{
include_once 'creole/drivers/odbc/metadata/ODBCTableInfo.php';
$result = @odbc_tables($this->conn->getResource());
if (!$result)
throw new SQLException('Could not list tables', $this->conn->nativeError());
while (odbc_fetch_row($result))
{
$tablename = strtoupper(odbc_result($result, 'TABLE_NAME'));
$this->tables[$tablename] = new ODBCTableInfo($this, $tablename);
}
@odbc_free_result($result);
}
/**
* @return void
* @throws SQLException
*/
protected function initSequences()
{
// Not sure how this is used yet.
}
}

View File

@ -0,0 +1,141 @@
<?php
/*
* $Id: ODBCTableInfo.php,v 1.2 2006/01/17 19:44:39 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/TableInfo.php';
/**
* ODBC implementation of TableInfo.
*
* @author Dave Lawson <dlawson@masterytech.com>
* @version $Revision: 1.2 $
* @package creole.drivers.odbc.metadata
*/
class ODBCTableInfo extends TableInfo {
/**
* @see TableInfo::initColumns()
*/
protected function initColumns()
{
include_once 'creole/metadata/ColumnInfo.php';
include_once 'creole/drivers/odbc/ODBCTypes.php';
ODBCTypes::loadTypeMap($this->conn);
$result = @odbc_columns($this->conn->getResource(), $this->dbname, '', $this->name);
if (!$result)
throw new SQLException('Could not get column names', $this->conn->nativeError());
while (odbc_fetch_row($result))
{
$name = odbc_result($result, 'COLUMN_NAME');
$type = odbc_result($result, 'TYPE_NAME');
$length = odbc_result($result, 'LENGTH');
$is_nullable = odbc_result($result, 'NULLABLE');
$default = '';
$precision = odbc_result($result, 'PRECISION');
$scale = odbc_result($result, 'SCALE');
$this->columns[$name] = new ColumnInfo($this, $name, ODBCTypes::getType($type), $type, $length, $precision, $scale, $is_nullable, $default);
}
@odbc_free_result($result);
$this->colsLoaded = true;
}
/**
* @see TableInfo::initPrimaryKey()
*/
protected function initPrimaryKey()
{
include_once 'creole/metadata/PrimaryKeyInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
$result = @odbc_primarykeys($this->conn->getResource(), $this->dbname, '', $this->name);
while (odbc_fetch_row($result))
{
$name = odbc_result($result, 'COLUMN_NAME');
if (!isset($this->primaryKey))
$this->primaryKey = new PrimaryKeyInfo($name);
$this->primaryKey->addColumn($this->columns[$name]);
}
@odbc_free_result($result);
$this->pkLoaded = true;
}
/**
* @see TableInfo::initIndexes()
*/
protected function initIndexes()
{
// Not sure if this can be implemented in a driver-independent way.
}
/**
* @see TableInfo::initForeignKeys()
*/
protected function initForeignKeys()
{
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
$result = @odbc_foreignkeys($this->conn->getResource(), '', '', '', $this->dbname, '', $this->name);
while (odbc_fetch_row($result))
{
$name = odbc_result($result, 'COLUMN_NAME');
$ftbl = odbc_result($result, 'FKTABLE_NAME');
$fcol = odbc_result($result, 'FKCOLUMN_NAME');
if (!isset($this->foreignKeys[$name]))
{
$this->foreignKeys[$name] = new ForeignKeyInfo($name);
if (($foreignTable = $this->database->getTable($ftbl)) === null)
{
$foreignTable = new TableInfo($ltbl);
$this->database->addTable($foreignTable);
}
if (($foreignCol = $foreignTable->getColumn($name)) === null)
{
$foreignCol = new ColumnInfo($foreignTable, $name);
$foreignTable->addColumn($foreignCol);
}
$this->foreignKeys[$name]->addReference($this->columns[$name], $foreignCol);
}
}
@odbc_free_result($result);
$this->fksLoaded = true;
}
}

View File

@ -0,0 +1,398 @@
<?php
/**
* $Id: OCI8Connection.php,v 1.18 2005/10/17 19:03:51 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Connection.php';
require_once 'creole/common/ConnectionCommon.php';
include_once 'creole/drivers/oracle/OCI8ResultSet.php';
/**
* Oracle implementation of Connection.
*
* @author David Giffin <david@giffin.org>
* @author Hans Lellelid <hans@xmpl.org>
* @author Stig Bakken <ssb@fast.no>
* @author Lukas Smith
* @version $Revision: 1.18 $
* @package creole.drivers.oracle
*/
class OCI8Connection extends ConnectionCommon implements Connection
{
protected $lastStmt = null;
/**
* Auto commit mode for oci_execute
* @var int
*/
protected $execMode = OCI_COMMIT_ON_SUCCESS;
/**
* Connect to a database and log in as the specified user.
*
* @param array $dsn The data source hash.
* @param int $flags Any connection flags.
* @access public
* @throws SQLException
* @return void
*/
function connect( $dsninfo, $flags = 0 )
{
if ( !extension_loaded( 'oci8' ) )
{
throw new SQLException( 'oci8 extension not loaded' );
}
$this->dsn = $dsninfo;
$this->flags = $flags;
$persistent =
( $flags & Creole::PERSISTENT === Creole::PERSISTENT );
$user = $dsninfo[ 'username' ];
$pw = $dsninfo[ 'password' ];
$hostspec = $dsninfo[ 'hostspec' ];
$port = $dsninfo[ 'port' ];
$db = $dsninfo[ 'database' ];
$connect_function = ( $persistent )
? 'oci_pconnect'
: 'oci_connect';
$encoding = !empty($dsninfo['encoding']) ? $dsninfo['encoding'] : null;
@ini_set( 'track_errors', true );
if ( $hostspec && $port )
{
$hostspec .= ':' . $port;
}
if ( $db && $hostspec && $user && $pw )
{
$conn = @$connect_function( $user, $pw, "//$hostspec/$db", $encoding);
}
elseif ( $hostspec && $user && $pw )
{
$conn = @$connect_function( $user, $pw, $hostspec, $encoding );
}
elseif ( $user || $pw )
{
$conn = @$connect_function( $user, $pw, null, $encoding );
}
else
{
$conn = false;
}
@ini_restore( 'track_errors' );
if ( $conn == false )
{
$error = oci_error();
$error = ( is_array( $error ) )
? $error[ 'message' ]
: null;
throw new SQLException( 'connect failed', $error );
}
$this->dblink = $conn;
//connected ok, need to set a few environment settings
//please note, if this is changed, the function setTimestamp and setDate in OCI8PreparedStatement.php
//must be changed to match
$sql = "ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'";
$this->executeQuery($sql);
}
/**
* @see Connection::disconnect()
*/
function close()
{
$ret = @oci_close( $this->dblink );
$this->dblink = null;
return $ret;
}
/**
* @see Connection::executeQuery()
*/
function executeQuery( $sql, $fetchmode = null )
{
$this->lastQuery = $sql;
// $result = @oci_parse( $this->dblink, $sql );
$result = oci_parse( $this->dblink, $sql );
if ( ! $result )
{
throw new SQLException( 'Unable to prepare query'
, $this->nativeError()
, $sql
);
}
$success = oci_execute( $result, $this->execMode );
if ( ! $success )
{
throw new SQLException( 'Unable to execute query'
, $this->nativeError( $result )
, $sql
);
}
return new OCI8ResultSet( $this, $result, $fetchmode );
}
/**
* @see Connection::simpleUpdate()
*/
function executeUpdate( $sql )
{
$this->lastQuery = $sql;
$statement = oci_parse( $this->dblink, $sql );
if ( ! $statement )
{
throw new SQLException( 'Unable to prepare update'
, $this->nativeError()
, $sql
);
}
$success = oci_execute( $statement, $this->execMode );
if ( ! $success )
{
throw new SQLException( 'Unable to execute update'
, $this->nativeError( $statement )
, $sql
);
}
$this->lastStmt = $statement;
return oci_num_rows( $statement );
}
/**
* Start a database transaction.
* @throws SQLException
* @return void
*/
protected function beginTrans()
{
$this->execMode = OCI_DEFAULT;
}
/**
* Commit the current transaction.
* @throws SQLException
* @return void
*/
protected function commitTrans()
{
$result = oci_commit( $this->dblink );
if ( ! $result )
{
throw new SQLException( 'Unable to commit transaction'
, $this->nativeError()
);
}
$this->execMode = OCI_COMMIT_ON_SUCCESS;
}
/**
* Roll back ( undo ) the current transaction.
* @throws SQLException
* @return void
*/
protected function rollbackTrans()
{
$result = oci_rollback( $this->dblink );
if ( ! $result )
{
throw new SQLException( 'Unable to rollback transaction'
, $this->nativeError()
);
}
$this->execMode = OCI_COMMIT_ON_SUCCESS;
}
/**
* Gets the number of rows affected by the data manipulation
* query.
*
* @return int Number of rows affected by the last query.
* @todo -cOCI8Connection Figure out whether getUpdateCount() should throw exception on error or just return 0.
*/
function getUpdateCount()
{
if ( ! $this->lastStmt )
{
return 0;
}
$result = oci_num_rows( $this->lastStmt );
if ( $result === false )
{
throw new SQLException( 'Update count failed'
, $this->nativeError( $this->lastStmt )
);
}
return $result;
}
/**
* Build Oracle-style query with limit or offset.
* If the original SQL is in variable: query then the requlting
* SQL looks like this:
* <pre>
* SELECT B.* FROM (
* SELECT A.*, rownum as TORQUE$ROWNUM FROM (
* query
* ) A
* ) B WHERE B.TORQUE$ROWNUM > offset AND B.TORQUE$ROWNUM
* <= offset + limit
* </pre>
*
* @param string &$sql the query
* @param int $offset
* @param int $limit
* @return void ( $sql parameter is currently manipulated directly )
*/
public function applyLimit( &$sql, $offset, $limit )
{
$sql =
'SELECT B.* FROM ( '
. 'SELECT A.*, rownum AS CREOLE$ROWNUM FROM ( '
. $sql
. ' ) A '
. ' ) B WHERE ';
if ( $offset > 0 )
{
$sql .= ' B.CREOLE$ROWNUM > ' . $offset;
if ( $limit > 0 )
{
$sql .= ' AND B.CREOLE$ROWNUM <= '
. ( $offset + $limit );
}
}
else
{
$sql .= ' B.CREOLE$ROWNUM <= ' . $limit;
}
}
/**
* Get the native Oracle Error Message as a string.
*
* @param string $msg The Internal Error Message
* @param mixed $errno The Oracle Error resource
*/
public function nativeError( $result = null )
{
if ( $result !== null )
{
$error = oci_error( $result );
}
else
{
$error = oci_error( $this->dblink );
}
return $error[ 'code' ] . ': ' . $error[ 'message' ];
}
/**
* @see Connection::getDatabaseInfo()
*/
public function getDatabaseInfo()
{
require_once 'creole/drivers/oracle/metadata/OCI8DatabaseInfo.php';
return new OCI8DatabaseInfo( $this );
}
/**
* @see Connection::getIdGenerator()
*/
public function getIdGenerator()
{
require_once 'creole/drivers/oracle/OCI8IdGenerator.php';
return new OCI8IdGenerator( $this );
}
/**
* Oracle supports native prepared statements, but the oci_parse call
* is actually called by the OCI8PreparedStatement class because
* some additional SQL processing may be necessary ( e.g. to apply limit ).
* @see OCI8PreparedStatement::executeQuery()
* @see OCI8PreparedStatement::executeUpdate()
* @see Connection::prepareStatement()
*/
public function prepareStatement( $sql )
{
require_once 'creole/drivers/oracle/OCI8PreparedStatement.php';
return new OCI8PreparedStatement( $this, $sql );
}
/**
* @see Connection::prepareCall()
*/
public function prepareCall( $sql )
{
throw new SQLException( 'Oracle driver does not yet support stored procedures using CallableStatement.' );
}
/**
* @see Connection::createStatement()
*/
public function createStatement()
{
require_once 'creole/drivers/oracle/OCI8Statement.php';
return new OCI8Statement( $this );
}
}

View File

@ -0,0 +1,65 @@
<?php
require_once 'creole/IdGenerator.php';
/**
* Oracle (OCI8) IdGenerator implimenation.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.5 $
* @package creole.drivers.oracle
*/
class OCI8IdGenerator implements IdGenerator {
/** Connection object that instantiated this class */
private $conn;
/**
* Creates a new IdGenerator class, saves passed connection for use
* later by getId() method.
* @param Connection $conn
*/
public function __construct(Connection $conn)
{
$this->conn = $conn;
}
/**
* @see IdGenerator::isBeforeInsert()
*/
public function isBeforeInsert()
{
return true;
}
/**
* @see IdGenerator::isAfterInsert()
*/
public function isAfterInsert()
{
return false;
}
/**
* @see IdGenerator::getIdMethod()
*/
public function getIdMethod()
{
return self::SEQUENCE;
}
/**
* @see IdGenerator::getId()
*/
public function getId($name = null)
{
if ($name === null) {
throw new SQLException("You must specify the sequence name when calling getId() method.");
}
$rs = $this->conn->executeQuery("select " . $name . ".nextval from dual", ResultSet::FETCHMODE_NUM);
$rs->next();
return $rs->getInt(1);
}
}

View File

@ -0,0 +1,424 @@
<?php
/*
* $Id: OCI8PreparedStatement.php,v 1.26 2006/01/30 21:32:05 sethr Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/PreparedStatement.php';
require_once 'creole/common/PreparedStatementCommon.php';
/**
* Oracle (OCI8) implementation of PreparedStatement.
*
* @author David Giffin <david@giffin.org>
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.26 $
* @package creole.drivers.oracle
*/
class OCI8PreparedStatement extends PreparedStatementCommon implements PreparedStatement {
/**
* Descriptor holders for LOB values.
* There are other types of descriptors, but we need to keep
* them separate, because we need to execute the save()/savefile() method
* on lob descriptors.
* @var array object from oci_new_descriptor
*/
private $lobDescriptors = array();
/**
* Hold any Blob/Clob data.
* These can be matched (by key) to descriptors in $lobDescriptors.
* @var array Lob[]
*/
private $lobs = array();
/**
* Array to store the columns in an insert or update statement.
* This is necessary for the proper handling of lob variables
* @var arrary columns[]
*/
private $columns = array();
/**
* If the statement is set, free it.
* @see PreparedStatement::close()
*/
function close()
{
if (isset($this->stmt))
@oci_free_statement($this->stmt);
}
/**
* Nothing to do - since oci_bind is used to insert data, no escaping is needed
* @param string $str
* @return string
*/
protected function escape($str)
{
return $str;
}
/**
* Executes the SQL query in this PreparedStatement object and returns the resultset generated by the query.
* @param mixed $p1 Either (array) Parameters that will be set using PreparedStatement::set() before query is executed or (int) fetchmode.
* @param int $fetchmode The mode to use when fetching the results (e.g. ResultSet::FETCHMODE_NUM, ResultSet::FETCHMODE_ASSOC).
* @return ResultSet
* @throws SQLException if a database access error occurs.
*/
public function executeQuery($p1 = null, $fetchmode = null)
{
$params = null;
if ($fetchmode !== null) {
$params = $p1;
} elseif ($p1 !== null) {
if (is_array($p1)) $params = $p1;
else $fetchmode = $p1;
}
if ($params) {
for($i=0,$cnt=count($params); $i < $cnt; $i++) {
$this->set($i+1, $params[$i]);
}
}
$this->updateCount = null; // reset
$sql = $this->sqlToOracleBindVars($this->sql);
if ($this->limit > 0 || $this->offset > 0) {
$this->conn->applyLimit($sql, $this->offset, $this->limit);
}
$result = oci_parse($this->conn->getResource(), $sql);
if (!$result) {
throw new SQLException("Unable to prepare query", $this->conn->nativeError(), $this->sqlToOracleBindVars($this->sql));
}
// bind all variables
$this->bindVars($result);
$success = oci_execute($result, OCI_DEFAULT);
if (!$success) {
throw new SQLException("Unable to execute query", $this->conn->nativeError($result), $this->sqlToOracleBindVars($this->sql));
}
$this->resultSet = new OCI8ResultSet($this->conn, $result, $fetchmode);
return $this->resultSet;
}
/**
* Executes the SQL INSERT, UPDATE, or DELETE statement in this PreparedStatement object.
*
* @param array $params Parameters that will be set using PreparedStatement::set() before query is executed.
* @return int Number of affected rows (or 0 for drivers that return nothing).
* @throws SQLException if a database access error occurs.
*/
public function executeUpdate($params = null)
{
if ($params) {
for($i=0,$cnt=count($params); $i < $cnt; $i++) {
$this->set($i+1, $params[$i]);
}
}
if($this->resultSet) $this->resultSet->close();
$this->resultSet = null; // reset
$stmt = oci_parse($this->conn->getResource(), $this->sqlToOracleBindVars($this->sql));
if (!$stmt) {
throw new SQLException("Unable to prepare update", $this->conn->nativeError(), $this->sqlToOracleBindVars($this->sql));
}
// bind all variables
$this->bindVars($stmt);
// Even if autocommit is on, delay commit until after LOBS have been saved
$success = oci_execute($stmt, OCI_DEFAULT);
if (!$success) {
throw new SQLException("Unable to execute update", $this->conn->nativeError($stmt), $this->sqlToOracleBindVars($this->sql));
}
// save data in any LOB descriptors, then free them
foreach($this->lobDescriptors as $paramIndex => $lobster) {
$lob = $this->lobs[$paramIndex]; // corresponding Blob/Clob
if ($lob->isFromFile()) {
$success = $lobster->savefile($lob->getInputFile());
} else {
$success = $lobster->save($lob->getContents());
}
if (!$success) {
$lobster->free();
throw new SQLException("Error saving lob bound to " . $paramIndex);
}
$lobster->free();
}
if ($this->conn->getAutoCommit()) {
oci_commit($this->conn->getResource()); // perform deferred commit
}
$this->updateCount = @oci_num_rows($stmt);
return $this->updateCount;
}
/**
* Performs the actual binding of variables using oci_bind_by_name().
*
* This may seem like useless overhead, but the reason why calls to oci_bind_by_name()
* are not performed in the set*() methods is that it is possible that the SQL will
* need to be modified -- e.g. by a setLimit() call -- and re-prepared. We cannot assume
* that the statement has been prepared when the set*() calls are invoked. This also means,
* therefore, that the set*() calls will not throw exceptions; all exceptions will be thrown
* when the statement is prepared.
*
* @param resource $stmt The statement result of oci_parse to use for binding.
* @return void
*/
private function bindVars($stmt)
{
foreach ($this->boundInVars as $idx => $val) {
$idxName = ":var" . $idx;
if (!oci_bind_by_name($stmt, $idxName, $this->boundInVars[$idx], -1)) {
throw new SQLException("Erorr binding value to placeholder " . $idx);
}
} // foreach
foreach ($this->lobs as $idx => $val) {
$idxName = ":var" . $idx;
if (class_exists('Blob') && $val instanceof Blob){
if (!oci_bind_by_name($stmt, $idxName, $this->lobDescriptors[$idx], -1, OCI_B_BLOB))
throw new SQLException("Erorr binding blob to placeholder " . $idx);
} elseif (class_exists('Clob') && $val instanceof Clob){
if (!oci_bind_by_name($stmt, $idxName, $this->lobDescriptors[$idx], -1, OCI_B_CLOB))
throw new SQLException("Erorr binding clob to placeholder " . $idx);
}
} // foreach
}
/**
* Convert a Propel SQL into Oracle SQL
*
* Look for all of the '?' and replace with ":varX"
*
* @param string $sql SQL in Propel native format
* @return string SQL in Oracle Bind Var format
* @todo -cOCI8PreparedStatement Consider changing this implementation to use the fact that we
* already know where all the '?' chars are (in $positions array).
*/
private function sqlToOracleBindVars($sql)
{
$out = "";
$in_literal = 0;
$idxNum = 1;
for ($i = 0; $i < strlen($sql); $i++) {
$char = $sql[$i];
if (strcmp($char,"'")==0) {
$in_literal = ~$in_literal;
}
if (strcmp($char,"?")==0 && !$in_literal) {
if (array_key_exists($idxNum, $this->lobs)){
if (class_exists('Blob') && ($this->lobs[$idxNum] instanceof Blob))
$out .= "empty_blob()";
if (class_exists('Clob') && ($this->lobs[$idxNum] instanceof Clob))
$out .= "empty_clob()";
} else
$out .= ":var" . $idxNum;
$idxNum++;
} else {
$out .= $char;
}
}
if (isset($this->lobs) && !empty($this->lobs)) {
$this->setColumnArray();
$retstmt = " Returning ";
$collist = "";
$bindlist = "";
foreach ($this->lobs as $idx=>$val) {
$idxName = ":var" . $idx;
if ((class_exists('Blob') && $val instanceof Blob) || (class_exists('Clob') && $val instanceof Clob)) {
//the columns array starts at zero instead of 1 like the lobs array
$collist .= $this->columns[$idx-1] . ",";
$bindlist .= $idxName . ",";
}
}
if (!empty($collist))
$out .= $retstmt . rtrim($collist, ",") . " into " . rtrim($bindlist, ",");
}
return $out;
}
/**
* @param string $paramIndex
* @param mixed $blob Blob object or string containing data.
* @return void
*/
function setBlob($paramIndex, $blob)
{
require_once 'creole/util/Blob.php';
if (!($blob instanceof Blob)) {
$b = new Blob();
$b->setContents($blob);
$blob = $b;
}
$this->lobDescriptors[$paramIndex] = oci_new_descriptor($this->conn->getResource(), OCI_D_LOB);
$this->lobs[$paramIndex] = $blob;
}
/**
* @param string $paramIndex
* @param mixed $clob Clob object or string containing data.
* @return void
*/
function setClob($paramIndex, $clob)
{
require_once 'creole/util/Clob.php';
if (!($clob instanceof Clob)) {
$c = new Clob();
$c->setContents($clob);
$clob = $c;
}
$this->lobDescriptors[$paramIndex] = oci_new_descriptor($this->conn->getResource(), OCI_D_LOB);
$this->lobs[$paramIndex] = $clob;
}
/**
* Since bind variables in oracle have no special characters, this setString method differs from the
* common one in that it does not single quote strings.
*
* @param int $paramIndex
* @param string $value
* @return void
*/
function setString($paramIndex, $value)
{
if ($value === null) {
$this->setNull($paramIndex);
} else {
// it's ok to have a fatal error here, IMO, if object doesn't have
// __toString() and is being passed to this method.
if ( is_object ( $value ) ) {
$this->boundInVars[$paramIndex] = $value->__toString();
} else {
$this->boundInVars[$paramIndex] = (string)$value;
}
}
}
/**
* Copied this function from common/PreparedStatement.php and modified to work with Oracle
* Please note the format used with date() matches that of NLS_DATE_FORMAT set in
* OCI8Connection.php
*
* @param int $paramIndex
* @param string $value
* @return void
*/
function setTimestamp($paramIndex, $value)
{
if ($value === null) {
$this->setNull($paramIndex);
} else {
if (is_numeric($value)) $value = date('Y-m-d H:i:s', $value);
elseif (is_object($value)) $value = date('Y-m-d H:i:s', $value->getTime());
$this->boundInVars[$paramIndex] = $value;
}
}
/**
* Please note the format used with date() matches that of NLS_DATE_FORMAT set in
* OCI8Connection.php
*
* @param int $paramIndex
* @param string $value
* @return void
*/
function setDate($paramIndex, $value)
{
if ($value === null) {
$this->setNull($paramIndex);
} else {
if (is_numeric($value)) $value = date("Y-m-d", $value);
elseif (is_object($value)) $value = date("Y-m-d", $value->getTime());
$this->boundInVars[$paramIndex] = $value;
}
}
/**
* In order to send lob data (clob/blob) to the Oracle data base, the
* sqlToOracleBindVars function needs to have an ordered list of the
* columns being addressed in the sql statement.
* Since only insert and update statements require special handling,
* there are two ways to find the columns:
* 1) find the first set of () and parse out the columns names based on
* the token ','
* 2) find all the text strings to the left of the equal signs.
*
* @param void
* @return void
*/
private function setColumnArray()
{
$this->columns = array();
//handle the simple insert case first
if(strtoupper(substr($this->sql, 0, 6)) == 'INSERT') {
$firstPos = strpos($this->sql, '(');
$secPos = strpos($this->sql, ')');
$collist = substr($this->sql, $firstPos + 1, $secPos - $firstPos - 1);
$this->columns = explode(',', $collist);
}
if (strtoupper(substr($this->sql, 0, 6)) == 'UPDATE') {
//handle more complex update case
//first get the string setup so we can explode based on '=?'
//second split results from previous action based on ' '
// the last token from this should be a column name
$tmp = $this->sql;
$tmp = str_replace(" =", "=", $this->sql);
$tmp = str_replace("= ", "=", $tmp);
$tmp = str_replace(",", " ", $tmp);
$stage1 = explode("=?",$tmp);
foreach($stage1 as $chunk) {
$stage2 = explode(' ', $chunk);
$this->columns[count($this->columns)] = $stage2[count($stage2) - 1];
}
}
}
/**
* @param int $paramIndex
* @return void
*/
function setNull($paramIndex)
{
$this->boundInVars[$paramIndex] = '';
}
}

View File

@ -0,0 +1,131 @@
<?php
/*
* $Id: OCI8ResultSet.php,v 1.13 2006/01/17 19:44:40 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/ResultSet.php';
require_once 'creole/common/ResultSetCommon.php';
/**
* Oracle (OCI8) implementation of ResultSet class.
*
* @author David Giffin <david@giffin.org>
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.13 $
* @package creole.drivers.oracle
*/
class OCI8ResultSet extends ResultSetCommon implements ResultSet
{
/**
* @see ResultSet::seek()
*/
function seek($rownum)
{
if ( $rownum < $this->cursorPos )
{
// this will effectively disable previous(), first() and some calls to relative() or absolute()
throw new SQLException( 'Oracle ResultSet is FORWARD-ONLY' );
}
// Oracle has no seek function imulate it here
while ( $this->cursorPos < $rownum )
{
$this->next();
}
$this->cursorPos = $rownum;
return true;
}
/**
* @see ResultSet::next()
*/
function next()
{
// no specific result position available
// Returns an array, which corresponds to the next result row or FALSE
// in case of error or there is no more rows in the result.
$this->fields = oci_fetch_array( $this->result
, $this->fetchmode
+ OCI_RETURN_NULLS
+ OCI_RETURN_LOBS
);
if ( ! $this->fields )
{
// grab error via array
$error = oci_error( $this->result );
if ( ! $error )
{
// end of recordset
$this->afterLast();
return false;
}
else
{
throw new SQLException( 'Error fetching result'
, $error[ 'code' ] . ': ' . $error[ 'message' ]
);
}
}
// Oracle returns all field names in uppercase and associative indices
// in the result array will be uppercased too.
if ($this->fetchmode === ResultSet::FETCHMODE_ASSOC && $this->lowerAssocCase)
{
$this->fields = array_change_key_case($this->fields, CASE_LOWER);
}
// Advance cursor position
$this->cursorPos++;
return true;
}
/**
* @see ResultSet::getRecordCount()
*/
function getRecordCount()
{
$rows = oci_num_rows( $this->result );
if ( $rows === false )
{
throw new SQLException( 'Error fetching num rows'
, $this->conn->nativeError( $this->result )
);
}
return ( int ) $rows;
}
/**
* @see ResultSet::close()
*/
function close()
{
$this->fields = array();
@oci_free_statement( $this->result );
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
* $Id: OCI8Statement.php,v 1.2 2004/03/05 15:46:12 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Statement.php';
require_once 'creole/common/StatementCommon.php';
/**
* Oracle (OCI8) Statement implementation.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.2 $
* @package creole.drivers.oracle
*/
class OCI8Statement extends StatementCommon implements Statement {
}

View File

@ -0,0 +1,90 @@
<?php
/*
* $Id: OCI8Types.php,v 1.8 2004/03/20 04:16:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/CreoleTypes.php';
/**
* Oracle types / type map.
*
* @author David Giffin <david@giffin.org>
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.8 $
* @package creole.drivers.oracle
*/
class OCI8Types extends CreoleTypes {
/** Map Oracle native types to Creole (JDBC) types. */
private static $typeMap = array(
'char' => CreoleTypes::CHAR,
'varchar2' => CreoleTypes::VARCHAR,
'long' => CreoleTypes::LONGVARCHAR,
'number' => CreoleTypes::NUMERIC,
'float' => CreoleTypes::FLOAT,
'integer' => CreoleTypes::INTEGER,
'smallint' => CreoleTypes::SMALLINT,
'double' => CreoleTypes::DOUBLE,
'raw' => CreoleTypes::VARBINARY,
'longraw' => CreoleTypes::LONGVARBINARY,
'date' => CreoleTypes::DATE,
'timestamp' => CreoleTypes::TIMESTAMP,
'blob' => CreoleTypes::BLOB,
'clob' => CreoleTypes::CLOB,
'varray' => CreoleTypes::ARR,
);
/** Reverse mapping, created on demand. */
private static $reverseMap = null;
/**
* This method returns the generic Creole (JDBC-like) type
* when given the native db type.
* @param string $nativeType DB native type (e.g. 'TEXT', 'byetea', etc.).
* @return int Creole native type (e.g. CreoleTypes::LONGVARCHAR, CreoleTypes::BINARY, etc.).
*/
public static function getType($nativeType)
{
$t = str_replace(' ', '', strtolower($nativeType));
if ( substr($t, 0, 9) == 'timestamp' ) return CreoleTypes::TIMESTAMP;
if (isset(self::$typeMap[$t])) {
return self::$typeMap[$t];
} else {
return CreoleTypes::OTHER;
}
}
/**
* This method will return a native type that corresponds to the specified
* Creole (JDBC-like) type.
* If there is more than one matching native type, then the LAST defined
* native type will be returned.
* @param int $creoleType
* @return string Native type string.
*/
public static function getNativeType($creoleType)
{
if (self::$reverseMap === null) {
self::$reverseMap = array_flip(self::$typeMap);
}
return @self::$reverseMap[$creoleType];
}
}

View File

@ -0,0 +1,90 @@
<?php
/*
* $Id: OCI8DatabaseInfo.php,v 1.11 2006/01/17 19:44:40 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/DatabaseInfo.php';
/**
* Oracle (OCI8) implementation of DatabaseInfo.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.11 $
* @package creole.drivers.oracle.metadata
*/
class OCI8DatabaseInfo extends DatabaseInfo {
private $schema;
public function __construct(Connection $conn) {
parent::__construct($conn);
$dsn = $conn->getDSN();
if (isset($dsn['schema'])) {
$this->schema = $dsn['schema'];
} else {
// For Changing DB/Schema in Meta Data Interface
$this->schema = $dsn['username'];
}
$this->schema = strtoupper( $this->schema );
}
public function getSchema() {
return $this->schema;
}
/**
* @throws SQLException
* @return void
*/
protected function initTables()
{
include_once 'creole/drivers/oracle/metadata/OCI8TableInfo.php';
$sql = "SELECT table_name
FROM all_tables
WHERE owner = '{$this->schema}'";
$statement = @oci_parse($this->conn->getResource(),$sql);
$success = @oci_execute($statement,OCI_DEFAULT);
if (!$success) {
throw new SQLException("Could not get tables", $this->conn->getResource()->nativeError($statement));
}
while ( $statement && $row = oci_fetch_assoc( $statement ) )
{
$row = array_change_key_case($row,CASE_LOWER);
$this->tables[strtoupper($row['table_name'])] = new OCI8TableInfo($this,$row['table_name']);
}
}
/**
* Oracle supports sequences.
*
* @return void
* @throws SQLException
*/
protected function initSequences()
{
// throw new SQLException("MySQL does not support sequences natively.");
}
}

View File

@ -0,0 +1,273 @@
<?php
/*
* $Id: OCI8TableInfo.php,v 1.13 2006/01/06 00:02:38 sethr Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/TableInfo.php';
/**
* Oracle (OCI8) implementation of TableInfo.
*
* @author David Giffin <david@giffin.org>
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision$
* @package creole.drivers.oracle.metadata
*/
class OCI8TableInfo extends TableInfo {
private $schema;
public function __construct(OCI8DatabaseInfo $database, $name)
{
$this->schema = strtoupper( $database->getSchema() );
parent::__construct($database, $name);
$this->name = strtoupper( $this->name );
}
/** Loads the columns for this table. */
protected function initColumns()
{
include_once 'creole/metadata/ColumnInfo.php';
include_once 'creole/drivers/oracle/OCI8Types.php';
// To get all of the attributes we need, we'll actually do
// two separate queries. The first gets names and default values
// the second will fill in some more details.
$sql = "
SELECT column_name
, data_type
, data_precision
, data_length
, data_default
, nullable
, data_scale
FROM all_tab_columns
WHERE table_name = '{$this->name}'
AND OWNER = '{$this->schema}'";
$statement = @oci_parse($this->conn->getResource(),$sql);
$success = @oci_execute($statement,OCI_DEFAULT);
if (!$success) {
throw new SQLException("Could Not Get Columns");
}
while ( $statement && $row = oci_fetch_array( $statement
, OCI_ASSOC + OCI_RETURN_NULLS ) ) {
$row = array_change_key_case($row, CASE_LOWER);
$this->columns[$row['column_name']] = new ColumnInfo( $this
, $row['column_name']
, OCI8Types::getType($row['data_type'])
, $row['data_type']
, $row['data_length']
, $row['data_precision']
, $row['data_scale']
, $row['nullable']
, $row['data_default']
);
}
$this->colsLoaded = true;
}
/** Loads the primary key information for this table. */
protected function initPrimaryKey()
{
include_once 'creole/metadata/PrimaryKeyInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
// Primary Keys Query
$sql = "SELECT a.owner, a.table_name,
a.constraint_name, a.column_name
FROM all_cons_columns a, all_constraints b
WHERE b.constraint_type = 'P'
AND a.constraint_name = b.constraint_name
AND b.table_name = '{$this->name}'
AND b.owner = '{$this->schema}'
";
$statement = @oci_parse($this->conn->getResource(),$sql);
$success = @oci_execute($statement,OCI_DEFAULT);
if (!$success) {
throw new SQLException("Could Not Get Primary Keys");
}
while ( $statement && $row = oci_fetch_assoc( $statement )) {
$row = array_change_key_case($row,CASE_LOWER);
$name = $row['column_name'];
if (!isset($this->primaryKey)) {
$this->primaryKey = new PrimaryKeyInfo($name);
}
$this->primaryKey->addColumn($this->columns[$name]);
}
$this->pkLoaded = true;
}
/** Loads the indexes for this table. */
protected function initIndexes() {
include_once 'creole/metadata/IndexInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
// Indexes
$sql = "SELECT
allind.index_name,
allind.table_name,
allind.index_type,
allind.uniqueness,
indcol.column_name
FROM all_indexes allind INNER JOIN all_ind_columns indcol
ON allind.owner = indcol.index_owner
AND allind.index_name = indcol.index_name
WHERE allind.table_owner = '{$this->schema}'
AND allind.table_name = '{$this->name}'
AND allind.index_name NOT IN (SELECT
constraint_name
FROM all_constraints
WHERE constraint_type = 'P')
ORDER BY allind.index_name,
indcol.column_position";
$statement = @oci_parse($this->conn->getResource(),$sql);
$success = @oci_execute($statement,OCI_DEFAULT);
if (!$success) {
throw new SQLException("Could Not Get Primary Keys");
}
// Loop through the returned results, grouping the same key_name together
// adding each column for that key.
while ( $statement && $row = oci_fetch_assoc( $statement )) {
$row = array_change_key_case($row,CASE_LOWER);
$name = $row['index_name'];
$index_col_name = $row['column_name'];
if (!isset($this->indexes[$name])) {
$this->indexes[$name] = new IndexInfo($name);
}
$this->indexes[$name]->addColumn($this->columns[ $index_col_name ]);
}
$this->indexesLoaded = true;
}
/** Load foreign keys */
protected function initForeignKeys() {
include_once 'creole/metadata/ForeignKeyInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
// Foreign keys
// TODO resolve cross schema references
// use all_cons... to do so, however, very slow queries then
// optimizations are very ugly
$sql = "
SELECT a.owner AS local_owner
, a.table_name AS local_table
, c.column_name AS local_column
, a.constraint_name AS foreign_key_name
, b.owner AS foreign_owner
, b.table_name AS foreign_table
, d.column_name AS foreign_column
, b.constraint_name AS foreign_constraint_name
, a.delete_rule AS on_delete
FROM user_constraints a
, user_constraints b
, user_cons_columns c
, user_cons_columns d
WHERE a.r_constraint_name = b.constraint_name
AND c.constraint_name = a.constraint_name
AND d.constraint_name = b.constraint_name
AND a.r_owner = b.owner
AND a.constraint_type='R'
AND a.table_name = '{$this->name}'
AND a.owner = '{$this->schema}'
";
$statement = @oci_parse($this->conn->getResource(),$sql);
$success = @oci_execute($statement,OCI_DEFAULT);
if (!$success) {
throw new SQLException("Could Not Get Primary Keys");
}
// Loop through the returned results, grouping the same key_name
// together adding each column for that key.
while ( $statement && $row = oci_fetch_assoc( $statement )) {
$row = array_change_key_case($row,CASE_LOWER);
$name = $row['foreign_key_name'];
$foreignTable = $this->database->getTable($row['foreign_table']);
$foreignColumn = $foreignTable->getColumn($row['foreign_column']);
$localTable = $this->database->getTable($row['local_table']);
$localColumn = $localTable->getColumn($row['local_column']);
if (!isset($this->foreignKeys[$name])) {
$this->foreignKeys[$name] = new ForeignKeyInfo($name);
}
switch ( $row[ 'on_delete' ] )
{
case 'CASCADE':
$onDelete = ForeignKeyInfo::CASCADE;
break;
case 'SET NULL':
$onDelete = ForeignKeyInfo::SETNULL;
break;
default:
case 'NO ACTION':
$onDelete = ForeignKeyInfo::NONE;
break;
}
// addReference( local, foreign, onDelete, onUpdate )
// Oracle doesn't support 'on update'
$this->foreignKeys[ $name ]->addReference(
$localColumn
, $foreignColumn
, $onDelete
);
}
$this->fksLoaded = true;
}
}

View File

@ -0,0 +1,260 @@
<?php
/*
* $Id: PgSQLConnection.php,v 1.21 2005/08/03 17:56:22 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Connection.php';
require_once 'creole/common/ConnectionCommon.php';
include_once 'creole/drivers/pgsql/PgSQLResultSet.php';
/**
* PgSQL implementation of Connection.
*
* @author Hans Lellelid <hans@xmpl.org> (Creole)
* @author Stig Bakken <ssb@fast.no> (PEAR::DB)
* @author Lukas Smith (PEAR::MDB)
* @version $Revision: 1.21 $
* @package creole.drivers.pgsql
*/
class PgSQLConnection extends ConnectionCommon implements Connection {
/**
* Affected Rows of last executed query.
* Postgres needs this for getUpdateCount()
* We used to store the entire result set
* instead but that can be a large dataset.
* @var int
*/
private $result_affected_rows;
/**
* Connect to a database and log in as the specified user.
*
* @param array $dsn The datasource hash.
* @param $flags Any connection flags.
* @access public
* @throws SQLException
* @return void
*/
function connect($dsninfo, $flags = 0)
{
global $php_errormsg;
if (!extension_loaded('pgsql')) {
throw new SQLException('pgsql extension not loaded');
}
$this->dsn = $dsninfo;
$this->flags = $flags;
$persistent = ($flags & Creole::PERSISTENT === Creole::PERSISTENT);
$protocol = (isset($dsninfo['protocol'])) ? $dsninfo['protocol'] : 'tcp';
$connstr = '';
if ($protocol == 'tcp') {
if (!empty($dsninfo['hostspec'])) {
$connstr = 'host=' . $dsninfo['hostspec'];
}
if (!empty($dsninfo['port'])) {
$connstr .= ' port=' . $dsninfo['port'];
}
}
if (isset($dsninfo['database'])) {
$connstr .= ' dbname=\'' . addslashes($dsninfo['database']) . '\'';
}
if (!empty($dsninfo['username'])) {
$connstr .= ' user=\'' . addslashes($dsninfo['username']) . '\'';
}
if (!empty($dsninfo['password'])) {
$connstr .= ' password=\'' . addslashes($dsninfo['password']) . '\'';
}
if (!empty($dsninfo['options'])) {
$connstr .= ' options=' . $dsninfo['options'];
}
if (!empty($dsninfo['tty'])) {
$connstr .= ' tty=' . $dsninfo['tty'];
}
if ($persistent) {
$conn = @pg_pconnect($connstr);
} else {
$conn = @pg_connect($connstr);
}
if (!$conn) {
// hide the password from connstr
$cleanconnstr = preg_replace('/password=\'.*?\'($|\s)/', 'password=\'*********\'', $connstr);
throw new SQLException('Could not connect', $php_errormsg, $cleanconnstr);
}
$this->dblink = $conn;
}
/**
* @see Connection::applyLimit()
*/
public function applyLimit(&$sql, $offset, $limit)
{
if ( $limit > 0 ) {
$sql .= " LIMIT ".$limit;
}
if ( $offset > 0 ) {
$sql .= " OFFSET ".$offset;
}
}
/**
* @see Connection::disconnect()
*/
function close()
{
$ret = @pg_close($this->dblink);
$this->result_affected_rows = null;
$this->dblink = null;
return $ret;
}
/**
* @see Connection::simpleQuery()
*/
function executeQuery($sql, $fetchmode = null)
{
$result = @pg_query($this->dblink, $sql);
if (!$result) {
throw new SQLException('Could not execute query', pg_last_error($this->dblink), $sql);
}
$this->result_affected_rows = (int) @pg_affected_rows($result);
return new PgSQLResultSet($this, $result, $fetchmode);
}
/**
* @see Connection::simpleUpdate()
*/
function executeUpdate($sql)
{
$result = @pg_query($this->dblink, $sql);
if (!$result) {
throw new SQLException('Could not execute update', pg_last_error($this->dblink), $sql);
}
$this->result_affected_rows = (int) @pg_affected_rows($result);
return $this->result_affected_rows;
}
/**
* Start a database transaction.
* @throws SQLException
* @return void
*/
protected function beginTrans()
{
$result = @pg_query($this->dblink, "BEGIN");
if (!$result) {
throw new SQLException('Could not begin transaction', pg_last_error($this->dblink));
}
}
/**
* Commit the current transaction.
* @throws SQLException
* @return void
*/
protected function commitTrans()
{
$result = @pg_query($this->dblink, "COMMIT");
if (!$result) {
throw new SQLException('Could not commit transaction', pg_last_error($this->dblink));
}
}
/**
* Roll back (undo) the current transaction.
* @throws SQLException
* @return void
*/
protected function rollbackTrans()
{
$result = @pg_query($this->dblink, "ROLLBACK");
if (!$result) {
throw new SQLException('Could not rollback transaction', pg_last_error($this->dblink));
}
}
/**
* Gets the number of rows affected by the data manipulation
* query.
* @see Statement::getUpdateCount()
* @return int Number of rows affected by the last query.
*/
function getUpdateCount()
{
if ( $this->result_affected_rows === null ) {
throw new SQLException('getUpdateCount called before any sql queries were executed');
}
return $this->result_affected_rows;
}
/**
* @see Connection::getDatabaseInfo()
*/
public function getDatabaseInfo()
{
require_once 'creole/drivers/pgsql/metadata/PgSQLDatabaseInfo.php';
return new PgSQLDatabaseInfo($this);
}
/**
* @see Connection::getIdGenerator()
*/
public function getIdGenerator()
{
require_once 'creole/drivers/pgsql/PgSQLIdGenerator.php';
return new PgSQLIdGenerator($this);
}
/**
* @see Connection::prepareStatement()
*/
public function prepareStatement($sql)
{
require_once 'creole/drivers/pgsql/PgSQLPreparedStatement.php';
return new PgSQLPreparedStatement($this, $sql);
}
/**
* @see Connection::prepareCall()
*/
public function prepareCall($sql) {
throw new SQLException('PostgreSQL does not support stored procedures.');
}
/**
* @see Connection::createStatement()
*/
public function createStatement()
{
require_once 'creole/drivers/pgsql/PgSQLStatement.php';
return new PgSQLStatement($this);
}
}

View File

@ -0,0 +1,84 @@
<?php
/*
* $Id: PgSQLIdGenerator.php,v 1.5 2004/03/19 14:19:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/IdGenerator.php';
/**
* PostgreSQL IdGenerator implemenation.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.5 $
* @package creole.drivers.pgsql
*/
class PgSQLIdGenerator implements IdGenerator {
/** Connection object that instantiated this class */
private $conn;
/**
* Creates a new IdGenerator class, saves passed connection for use
* later by getId() method.
* @param Connection $conn
*/
public function __construct(Connection $conn)
{
$this->conn = $conn;
}
/**
* @see IdGenerator::isBeforeInsert()
*/
public function isBeforeInsert()
{
return true;
}
/**
* @see IdGenerator::isAfterInsert()
*/
public function isAfterInsert()
{
return false;
}
/**
* @see IdGenerator::getIdMethod()
*/
public function getIdMethod()
{
return self::SEQUENCE;
}
/**
* @see IdGenerator::getId()
*/
public function getId($name = null)
{
if ($name === null) {
throw new SQLException("You must specify the sequence name when calling getId() method.");
}
$rs = $this->conn->executeQuery("SELECT nextval('" . pg_escape_string ( $name ) . "')", ResultSet::FETCHMODE_NUM);
$rs->next();
return $rs->getInt(1);
}
}

View File

@ -0,0 +1,157 @@
<?php
/*
* $Id: PgSQLPreparedStatement.php,v 1.14 2005/04/16 18:55:28 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/PreparedStatement.php';
require_once 'creole/common/PreparedStatementCommon.php';
/**
* PgSQL subclass for prepared statements.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.14 $
* @package creole.drivers.pgsql
*/
class PgSQLPreparedStatement extends PreparedStatementCommon implements PreparedStatement {
/**
* Quotes string using native pgsql function (pg_escape_string).
* @param string $str
* @return string
*/
protected function escape($str)
{
return pg_escape_string($str);
}
/**
* Recursive function to turn multi-dim array into str representation.
* @param array $arr
* @return string Array in pgsql-friendly string notation: {val1, val2} or {{sub1,sub2}, {sub3, sub4}}
*/
private function arrayToStr($arr)
{
$parts = array();
foreach((array)$arr as $el) {
if (is_array($el)) {
$parts[] = $this->arrayToStr($el);
} else {
if (is_string($el)) {
$parts[] = '"' . $this->escape($el) . '"';
} else {
$parts[] = $el;
}
}
}
return '{' . implode(',', $parts) . '}';
}
/**
* Sets an array.
* Unless a driver-specific method is used, this means simply serializing
* the passed parameter and storing it as a string.
* @param int $paramIndex
* @param array $value
* @return void
* @see PreparedStatement::setArray()
*/
function setArray($paramIndex, $value)
{
if( $paramIndex > $this->positionsCount || $paramIndex < 1) {
throw new SQLException('Cannot bind to invalid param index: '.$paramIndex);
}
if ($value === null)
$this->setNull($paramIndex);
else
$this->boundInVars[$paramIndex] = "'" . $this->arrayToStr($value) . "'";
}
/**
* For setting value of Postgres BOOLEAN column.
* @param int $paramIndex
* @param boolean $value
* @return void
*/
function setBoolean($paramIndex, $value)
{
if( $paramIndex > $this->positionsCount || $paramIndex < 1) {
throw new SQLException('Cannot bind to invalid param index: '.$paramIndex);
}
if ($value === null)
$this->setNull($paramIndex);
else
$this->boundInVars[$paramIndex] = ($value ? "'t'" : "'f'");
}
/**
* Applies sqlite_udf_encode_binary() to ensure that binary contents will be handled correctly by sqlite.
* @param int $paramIndex
* @param mixed $blob Blob object or string containing data.
* @return void
*/
function setBlob($paramIndex, $blob)
{
if ($blob === null) {
$this->setNull($paramIndex);
} else {
// they took magic __toString() out of PHP5.0.0; this sucks
if (is_object($blob)) {
$blob = $blob->__toString();
}
$this->boundInVars[$paramIndex] = "'" . pg_escape_bytea( $blob ) . "'";
}
}
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
function setTime($paramIndex, $value)
{
if ($value === null) {
$this->setNull($paramIndex);
} else {
if ( is_numeric ( $value ) ) {
$value = date ( "H:i:s O", $value );
} elseif ( is_object ( $value ) ) {
$value = date ( "H:i:s O", $value->getTime ( ) );
}
$this->boundInVars [ $paramIndex ] = "'" . $this->escape ( $value ) . "'";
}
}
/**
* @param int $paramIndex
* @param string $value
* @return void
*/
function setTimestamp($paramIndex, $value)
{
if ($value === null) {
$this->setNull($paramIndex);
} else {
if (is_numeric($value)) $value = date('Y-m-d H:i:s O', $value);
elseif (is_object($value)) $value = date("Y-m-d H:i:s O", $value->getTime());
$this->boundInVars[$paramIndex] = "'".$this->escape($value)."'";
}
}
}

View File

@ -0,0 +1,205 @@
<?php
/*
* $Id: PgSQLResultSet.php,v 1.31 2006/01/17 19:44:40 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/ResultSet.php';
require_once 'creole/common/ResultSetCommon.php';
/**
* PostgreSQL implementation of ResultSet.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.31 $
* @package creole.drivers.pgsql
*/
class PgSQLResultSet extends ResultSetCommon implements ResultSet {
/**
* Gets optimized PgSQLResultSetIterator.
* @return PgSQLResultSetIterator
*/
/*
public function getIterator()
{
require_once 'creole/drivers/pgsql/PgSQLResultSetIterator.php';
return new PgSQLResultSetIterator($this);
}
*/
/**
* Postgres doesn't actually move the db pointer. The specific row
* is fetched by call to pg_fetch_array() rather than by a seek and
* then an unspecified pg_fetch_array() call.
*
* The only side-effect of this situation is that we don't really know
* if the seek will fail or succeed until we have called next(). This
* behavior is acceptible - and explicitly documented in
* ResultSet::seek() phpdoc.
*
* @see ResultSet::seek()
*/
public function seek($rownum)
{
if ($rownum < 0) {
return false;
}
// PostgreSQL rows start w/ 0, but this works, because we are
// looking to move the position _before_ the next desired position
$this->cursorPos = $rownum;
return true;
}
/**
* @see ResultSet::next()
*/
public function next()
{
$this->fields = @pg_fetch_array($this->result, $this->cursorPos, $this->fetchmode);
if (!$this->fields) {
$err = @pg_result_error($this->result);
if (!$err) {
// We've advanced beyond end of recordset.
$this->afterLast();
return false;
} else {
throw new SQLException("Error fetching result", $err);
}
}
if ($this->fetchmode === ResultSet::FETCHMODE_ASSOC && $this->lowerAssocCase) {
$this->fields = array_change_key_case($this->fields, CASE_LOWER);
}
// Advance cursor position
$this->cursorPos++;
return true;
}
/**
* @see ResultSet::getRecordCount()
*/
public function getRecordCount()
{
$rows = @pg_num_rows($this->result);
if ($rows === null) {
throw new SQLException("Error fetching num rows", pg_result_error($this->result));
}
return (int) $rows;
}
/**
* @see ResultSet::close()
*/
public function close()
{
$this->fields = array();
@pg_free_result($this->result);
}
/**
* Convert Postgres string representation of array into native PHP array.
* @param string $str Postgres string array rep: {1223, 2343} or {{"welcome", "home"}, {"test2", ""}}
* @return array
*/
private function strToArray($str)
{
$str = substr($str, 1, -1); // remove { }
$res = array();
$subarr = array();
$in_subarr = 0;
$toks = explode(',', $str);
foreach($toks as $tok) {
if ($in_subarr > 0) { // already in sub-array?
$subarr[$in_subarr][] = $tok;
if ('}' === substr($tok, -1, 1)) { // check to see if we just added last component
$res[] = $this->strToArray(implode(',', $subarr[$in_subarr]));
$in_subarr--;
}
} elseif ($tok{0} === '{') { // we're inside a new sub-array
if ('}' !== substr($tok, -1, 1)) {
$in_subarr++;
// if sub-array has more than one element
$subarr[$in_subarr] = array();
$subarr[$in_subarr][] = $tok;
} else {
$res[] = $this->strToArray($tok);
}
} else { // not sub-array
$val = trim($tok, '"'); // remove " (surrounding strings)
// perform type castng here?
$res[] = $val;
}
}
return $res;
}
/**
* Reads a column as an array.
* The value of the column is unserialized & returned as an array.
* @param mixed $column Column name (string) or index (int) starting with 1.
* @return array
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getArray($column)
{
if (is_int($column)) { $column--; } // because Java convention is to start at 1
if (!array_key_exists($column, $this->fields)) { throw new SQLException("Invalid resultset column: " . (is_int($column) ? $column + 1 : $column)); }
if ($this->fields[$column] === null) { return null; }
return $this->strToArray($this->fields[$column]);
}
/**
* Returns Blob with contents of column value.
*
* @param mixed $column Column name (string) or index (int) starting with 1 (if ResultSet::FETCHMODE_NUM was used).
* @return Blob New Blob with data from column.
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getBlob($column)
{
if (is_int($column)) { $column--; } // because Java convention is to start at 1
if (!array_key_exists($column, $this->fields)) { throw new SQLException("Invalid resultset column: " . (is_int($column) ? $column + 1 : $column)); }
if ($this->fields[$column] === null) { return null; }
require_once 'creole/util/Blob.php';
$b = new Blob();
$b->setContents(pg_unescape_bytea($this->fields[$column]));
return $b;
}
/**
* @param mixed $column Column name (string) or index (int) starting with 1.
* @return boolean
* @throws SQLException - If the column specified is not a valid key in current field array.
*/
public function getBoolean($column)
{
if (is_int($column)) { $column--; } // because Java convention is to start at 1
if (!array_key_exists($column, $this->fields)) { throw new SQLException("Invalid resultset column: " . (is_int($column) ? $column + 1 : $column)); }
if ($this->fields[$column] === null) { return null; }
return ($this->fields[$column] === 't');
}
}

View File

@ -0,0 +1,109 @@
<?php
/*
* $Id: PgSQLResultSetIterator.php,v 1.1 2004/12/04 05:58:53 gamr Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Optimized iterator for PostgreSQL, based off of SQLite iterator.
* Testing with SeekableIterator, no idea if it will keep this
* functionality or what uses it or even how to use it as yet.
*
* @author Cameron Brunner <webmaster@animetorrents.com>
* @version $Revision: 1.1 $
* @package creole.drivers.pgsql
*/
class PgSQLResultSetIterator implements SeekableIterator, Countable {
private $result;
private $pos = 0;
private $fetchmode;
private $row_count;
private $rs;
/**
* Construct the iterator.
* @param PgSQLResultSet $rs
*/
public function __construct(PgSQLResultSet $rs)
{
$this->result = $rs->getResource();
$this->fetchmode = $rs->getFetchmode();
$this->row_count = $rs->getRecordCount();
$this->rs = $rs; // This is to address reference count bug: http://creole.phpdb.org/trac/ticket/6
}
/**
* This method actually has no effect, since we do not rewind ResultSet for iteration.
*/
function rewind()
{
$this->pos = 0;
}
function valid()
{
return ( $this->pos < $this->row_count );
}
/**
* Returns the cursor position. Note that this will not necessarily
* be 1 for the first row, since no rewind is performed at beginning
* of iteration.
* @return int
*/
function key()
{
return $this->pos;
}
/**
* Returns the row (assoc array) at current cursor pos.
* @return array
*/
function current()
{
return pg_fetch_array($this->result, $this->pos, $this->fetchmode);
}
/**
* Advances internal cursor pos.
*/
function next()
{
$this->pos++;
}
/**
* Sets cursor to specific value.
*/
function seek ( $index )
{
if ( ! is_int ( $index ) ) {
throw new InvalidArgumentException ( 'Invalid arguement to seek' );
}
if ( $index < 0 || $index > $this->row_count ) {
throw new OutOfBoundsException ( 'Invalid seek position' );
}
$this->pos = $index;
}
function count ( ) {
return $this->row_count;
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
* $Id: PgSQLStatement.php,v 1.1 2004/02/19 02:49:42 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Statement.php';
require_once 'creole/common/StatementCommon.php';
/**
* PostgreSQL Statement implementation.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.1 $
* @package creole.drivers.pgsql
*/
class PgSQLStatement extends StatementCommon implements Statement {
}

View File

@ -0,0 +1,101 @@
<?php
/*
* $Id: PgSQLTypes.php,v 1.8 2004/04/09 19:16:05 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/CreoleTypes.php';
/**
* PostgreSQL types / type map.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.8 $
* @package creole.drivers.pgsql
*/
class PgSQLTypes extends CreoleTypes {
/** Map PostgreSQL native types to Creole (JDBC) types. */
private static $typeMap = array (
"int2" => CreoleTypes::SMALLINT,
"int4" => CreoleTypes::INTEGER,
"oid" => CreoleTypes::INTEGER,
"int8" => CreoleTypes::BIGINT,
"cash" => CreoleTypes::DOUBLE,
"money" => CreoleTypes::DOUBLE,
"numeric" => CreoleTypes::NUMERIC,
"float4" => CreoleTypes::REAL,
"float8" => CreoleTypes::DOUBLE,
"bpchar" => CreoleTypes::CHAR,
"char" => CreoleTypes::CHAR,
"char2" => CreoleTypes::CHAR,
"char4" => CreoleTypes::CHAR,
"char8" => CreoleTypes::CHAR,
"char16" => CreoleTypes::CHAR,
"varchar" => CreoleTypes::VARCHAR,
"text" => CreoleTypes::VARCHAR,
"name" => CreoleTypes::VARCHAR,
"filename" => CreoleTypes::VARCHAR,
"bytea" => CreoleTypes::BINARY,
"bool" => CreoleTypes::BOOLEAN,
"date" => CreoleTypes::DATE,
"time" => CreoleTypes::TIME,
"abstime" => CreoleTypes::TIMESTAMP,
"timestamp" => CreoleTypes::TIMESTAMP,
"timestamptz" => CreoleTypes::TIMESTAMP,
"_bool" => CreoleTypes::ARR,
"_char" => CreoleTypes::ARR,
"_int2" => CreoleTypes::ARR,
"_int4" => CreoleTypes::ARR,
"_text" => CreoleTypes::ARR,
"_oid" => CreoleTypes::ARR,
"_varchar" => CreoleTypes::ARR,
"_int8" => CreoleTypes::ARR,
"_float4" => CreoleTypes::ARR,
"_float8" => CreoleTypes::ARR,
"_abstime" => CreoleTypes::ARR,
"_date" => CreoleTypes::ARR,
"_time" => CreoleTypes::ARR,
"_timestamp" => CreoleTypes::ARR,
"_numeric" => CreoleTypes::ARR,
"_bytea" => CreoleTypes::ARR,
);
/** Reverse lookup map, created on demand. */
private static $reverseMap = null;
public static function getType($pgsqlType)
{
$t = strtolower($pgsqlType);
if (isset(self::$typeMap[$t])) {
return self::$typeMap[$t];
} else {
return CreoleTypes::OTHER;
}
}
public static function getNativeType($creoleType)
{
if (self::$reverseMap === null) {
self::$reverseMap = array_flip(self::$typeMap);
}
return @self::$reverseMap[$creoleType];
}
}

View File

@ -0,0 +1,115 @@
<?php
/*
* $Id: PgSQLDatabaseInfo.php,v 1.11 2006/01/17 19:44:40 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/DatabaseInfo.php';
/**
* MySQL implementation of DatabaseInfo.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.11 $
* @package creole.drivers.pgsql.metadata
*/
class PgSQLDatabaseInfo extends DatabaseInfo {
/**
* @throws SQLException
* @return void
*/
protected function initTables()
{
include_once 'creole/drivers/pgsql/metadata/PgSQLTableInfo.php';
// Get Database Version
// TODO: www.php.net/pg_version
$result = pg_query ($this->conn->getResource(), "SELECT version() as ver");
if (!$result)
{
throw new SQLException ("Failed to select database version");
} // if (!$result)
$row = pg_fetch_assoc ($result, 0);
$arrVersion = sscanf ($row['ver'], '%*s %d.%d');
$version = sprintf ("%d.%d", $arrVersion[0], $arrVersion[1]);
// Clean up
$arrVersion = null;
$row = null;
pg_free_result ($result);
$result = null;
$result = pg_query($this->conn->getResource(), "SELECT oid, relname FROM pg_class
WHERE relkind = 'r' AND relnamespace = (SELECT oid
FROM pg_namespace
WHERE
nspname NOT IN ('information_schema','pg_catalog')
AND nspname NOT LIKE 'pg_temp%'
AND nspname NOT LIKE 'pg_toast%'
LIMIT 1)
ORDER BY relname");
if (!$result) {
throw new SQLException("Could not list tables", pg_last_error($this->dblink));
}
while ($row = pg_fetch_assoc($result)) {
$this->tables[strtoupper($row['relname'])] = new PgSQLTableInfo($this, $row['relname'], $version, $row['oid']);
}
$this->tablesLoaded = true;
}
/**
* PgSQL sequences.
*
* @return void
* @throws SQLException
*/
protected function initSequences()
{
$this->sequences = array();
$result = pg_query($this->conn->getResource(), "SELECT oid, relname FROM pg_class
WHERE relkind = 'S' AND relnamespace = (SELECT oid
FROM pg_namespace
WHERE
nspname NOT IN ('information_schema','pg_catalog')
AND nspname NOT LIKE 'pg_temp%'
AND nspname NOT LIKE 'pg_toast%'
LIMIT 1)
ORDER BY relname");
if (!$result) {
throw new SQLException("Could not list sequences", pg_last_error($this->dblink));
}
while ($row = pg_fetch_assoc($result)) {
// FIXME -- decide what info we need for sequences & then create a SequenceInfo object (if needed)
$obj = new stdClass;
$obj->name = $row['relname'];
$obj->oid = $row['oid'];
$this->sequences[strtoupper($row['relname'])] = $obj;
}
}
}

View File

@ -0,0 +1,423 @@
<?php
/*
* $Id: PgSQLTableInfo.php,v 1.31 2006/01/17 19:44:40 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/TableInfo.php';
/**
* PgSQL implementation of TableInfo.
*
* See this Python code by David M. Cook for some good reference on Pgsql metadata
* functions:
* @link http://www.sandpyt.org/pipermail/sandpyt/2003-March/000008.html
*
* Here's some more information from postgresql:
* @link http://developer.postgresql.org/docs/pgsql/src/backend/catalog/information_schema.sql
*
* @todo -c Eventually move to supporting only Postgres >= 7.4, which has the information_schema
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.31 $
* @package creole.drivers.pgsql.metadata
*/
class PgSQLTableInfo extends TableInfo {
/**
* Database Version.
* @var String
*/
private $version;
/**
* Table OID
* @var Integer
*/
private $oid;
/**
* @param string $table The table name.
* @param string $database The database name.
* @param resource $dblink The db connection resource.
*/
function __construct(DatabaseInfo $database, $name, $version, $intOID) {
parent::__construct ($database, $name);
$this->version = $version;
$this->oid = $intOID;
} // function __construct(DatabaseInfo $database, $name) {
/** Load the columns for this table */
protected function initColumns () {
// Include dependencies
include_once ('creole/metadata/ColumnInfo.php');
include_once ('creole/drivers/pgsql/PgSQLTypes.php');
// Get the columns, types, etc.
// Based on code from pgAdmin3 (http://www.pgadmin.org/)
$result = pg_query ($this->conn->getResource(), sprintf ("SELECT
att.attname,
att.atttypmod,
att.atthasdef,
att.attnotnull,
def.adsrc,
CASE WHEN att.attndims > 0 THEN 1 ELSE 0 END AS isarray,
CASE
WHEN ty.typname = 'bpchar'
THEN 'char'
WHEN ty.typname = '_bpchar'
THEN '_char'
ELSE
ty.typname
END AS typname,
ty.typtype
FROM pg_attribute att
JOIN pg_type ty ON ty.oid=att.atttypid
LEFT OUTER JOIN pg_attrdef def ON adrelid=att.attrelid AND adnum=att.attnum
WHERE att.attrelid = %d AND att.attnum > 0
AND att.attisdropped IS FALSE
ORDER BY att.attnum", $this->oid));
if (!$result) {
throw new SQLException("Could not list fields for table: " . $this->name, pg_last_error($this->conn->getResource()));
}
while($row = pg_fetch_assoc($result)) {
$size = null;
$precision = null;
$scale = null;
// Check to ensure that this column isn't an array data type
if (((int) $row['isarray']) === 1)
{
throw new SQLException (sprintf ("Array datatypes are not currently supported [%s.%s]", $this->name, $row['attname']));
} // if (((int) $row['isarray']) === 1)
$name = $row['attname'];
// If they type is a domain, Process it
if (strtolower ($row['typtype']) == 'd')
{
$arrDomain = $this->processDomain ($row['typname']);
$type = $arrDomain['type'];
$size = $arrDomain['length'];
$precision = $size;
$scale = $arrDomain['scale'];
$boolHasDefault = (strlen (trim ($row['atthasdef'])) > 0) ? $row['atthasdef'] : $arrDomain['hasdefault'];
$default = (strlen (trim ($row['adsrc'])) > 0) ? $row['adsrc'] : $arrDomain['default'];
$is_nullable = (strlen (trim ($row['attnotnull'])) > 0) ? $row['attnotnull'] : $arrDomain['notnull'];
$is_nullable = (($is_nullable == 't') ? false : true);
} // if (strtolower ($row['typtype']) == 'd')
else
{
$type = $row['typname'];
$arrLengthPrecision = $this->processLengthScale ($row['atttypmod'], $type);
$size = $arrLengthPrecision['length'];
$precision = $size;
$scale = $arrLengthPrecision['scale'];
$boolHasDefault = $row['atthasdef'];
$default = $row['adsrc'];
$is_nullable = (($row['attnotnull'] == 't') ? false : true);
} // else (strtolower ($row['typtype']) == 'd')
$autoincrement = null;
// if column has a default
if (($boolHasDefault == 't') && (strlen (trim ($default)) > 0))
{
if (!preg_match('/^nextval\(/', $default))
{
$strDefault= preg_replace ('/::[\W\D]*/', '', $default);
$default = str_replace ("'", '', $strDefault);
} // if (!preg_match('/^nextval\(/', $row['atthasdef']))
else
{
$autoincrement = true;
$default = null;
} // else
} // if (($boolHasDefault == 't') && (strlen (trim ($default)) > 0))
else
{
$default = null;
} // else (($boolHasDefault == 't') && (strlen (trim ($default)) > 0))
$this->columns[$name] = new ColumnInfo($this, $name, PgSQLTypes::getType($type), $type, $size, $precision, $scale, $is_nullable, $default, $autoincrement);
}
$this->colsLoaded = true;
} // protected function initColumns ()
private function processLengthScale ($intTypmod, $strName)
{
// Define the return array
$arrRetVal = array ('length'=>null, 'scale'=>null);
// Some datatypes don't have a Typmod
if ($intTypmod == -1)
{
return $arrRetVal;
} // if ($intTypmod == -1)
// Numeric Datatype?
if ($strName == PgSQLTypes::getNativeType (CreoleTypes::NUMERIC))
{
$intLen = ($intTypmod - 4) >> 16;
$intPrec = ($intTypmod - 4) & 0xffff;
$intLen = sprintf ("%ld", $intLen);
if ($intPrec)
{
$intPrec = sprintf ("%ld", $intPrec);
} // if ($intPrec)
$arrRetVal['length'] = $intLen;
$arrRetVal['scale'] = $intPrec;
} // if ($strName == PgSQLTypes::getNativeType (CreoleTypes::NUMERIC))
elseif ($strName == PgSQLTypes::getNativeType (CreoleTypes::TIME) || $strName == 'timetz'
|| $strName == PgSQLTypes::getNativeType (CreoleTypes::TIMESTAMP) || $strName == 'timestamptz'
|| $strName == 'interval' || $strName == 'bit')
{
$arrRetVal['length'] = sprintf ("%ld", $intTypmod);
} // elseif (TIME, TIMESTAMP, INTERVAL, BIT)
else
{
$arrRetVal['length'] = sprintf ("%ld", ($intTypmod - 4));
} // else
return $arrRetVal;
} // private function processLengthScale ($intTypmod, $strName)
private function processDomain ($strDomain)
{
if (strlen (trim ($strDomain)) < 1)
{
throw new SQLException ("Invalid domain name [" . $strDomain . "]");
} // if (strlen (trim ($strDomain)) < 1)
$result = pg_query ($this->conn->getResource(), sprintf ("SELECT
d.typname as domname,
b.typname as basetype,
d.typlen,
d.typtypmod,
d.typnotnull,
d.typdefault
FROM pg_type d
INNER JOIN pg_type b ON b.oid = CASE WHEN d.typndims > 0 then d.typelem ELSE d.typbasetype END
WHERE
d.typtype = 'd'
AND d.typname = '%s'
ORDER BY d.typname", $strDomain));
if (!$result) {
throw new SQLException("Query for domain [" . $strDomain . "] failed.", pg_last_error($this->conn->getResource()));
}
$row = pg_fetch_assoc ($result);
if (!$row)
{
throw new SQLException ("Domain [" . $strDomain . "] not found.");
} // if (!$row)
$arrDomain = array ();
$arrDomain['type'] = $row['basetype'];
$arrLengthPrecision = $this->processLengthScale ($row['typtypmod'], $row['basetype']);
$arrDomain['length'] = $arrLengthPrecision['length'];
$arrDomain['scale'] = $arrLengthPrecision['scale'];
$arrDomain['notnull'] = $row['typnotnull'];
$arrDomain['default'] = $row['typdefault'];
$arrDomain['hasdefault'] = (strlen (trim ($row['typdefault'])) > 0) ? 't' : 'f';
pg_free_result ($result);
return $arrDomain;
} // private function processDomain ($strDomain)
/** Load foreign keys for this table. */
protected function initForeignKeys()
{
include_once 'creole/metadata/ForeignKeyInfo.php';
$result = pg_query ($this->conn->getResource(), sprintf ("SELECT
conname,
confupdtype,
confdeltype,
cl.relname as fktab,
a2.attname as fkcol,
cr.relname as reftab,
a1.attname as refcol
FROM pg_constraint ct
JOIN pg_class cl ON cl.oid=conrelid
JOIN pg_class cr ON cr.oid=confrelid
LEFT JOIN pg_catalog.pg_attribute a1 ON a1.attrelid = ct.confrelid
LEFT JOIN pg_catalog.pg_attribute a2 ON a2.attrelid = ct.conrelid
WHERE
contype='f'
AND conrelid = %d
AND a2.attnum = ct.conkey[1]
AND a1.attnum = ct.confkey[1]
ORDER BY conname", $this->oid));
if (!$result) {
throw new SQLException("Could not list foreign keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
}
while($row = pg_fetch_assoc($result)) {
$name = $row['conname'];
$local_table = $row['fktab'];
$local_column = $row['fkcol'];
$foreign_table = $row['reftab'];
$foreign_column = $row['refcol'];
// On Update
switch ($row['confupdtype']) {
case 'c':
$onupdate = ForeignKeyInfo::CASCADE; break;
case 'd':
$onupdate = ForeignKeyInfo::SETDEFAULT; break;
case 'n':
$onupdate = ForeignKeyInfo::SETNULL; break;
case 'r':
$onupdate = ForeignKeyInfo::RESTRICT; break;
default:
case 'a':
//NOACTION is the postgresql default
$onupdate = ForeignKeyInfo::NONE; break;
}
// On Delete
switch ($row['confdeltype']) {
case 'c':
$ondelete = ForeignKeyInfo::CASCADE; break;
case 'd':
$ondelete = ForeignKeyInfo::SETDEFAULT; break;
case 'n':
$ondelete = ForeignKeyInfo::SETNULL; break;
case 'r':
$ondelete = ForeignKeyInfo::RESTRICT; break;
default:
case 'a':
//NOACTION is the postgresql default
$ondelete = ForeignKeyInfo::NONE; break;
}
$foreignTable = $this->database->getTable($foreign_table);
$foreignColumn = $foreignTable->getColumn($foreign_column);
$localTable = $this->database->getTable($local_table);
$localColumn = $localTable->getColumn($local_column);
if (!isset($this->foreignKeys[$name])) {
$this->foreignKeys[$name] = new ForeignKeyInfo($name);
}
$this->foreignKeys[$name]->addReference($localColumn, $foreignColumn, $ondelete, $onupdate);
}
$this->fksLoaded = true;
}
/** Load indexes for this table */
protected function initIndexes()
{
include_once 'creole/metadata/IndexInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
$result = pg_query ($this->conn->getResource(), sprintf ("SELECT
DISTINCT ON(cls.relname)
cls.relname as idxname,
indkey,
indisunique
FROM pg_index idx
JOIN pg_class cls ON cls.oid=indexrelid
WHERE indrelid = %d AND NOT indisprimary
ORDER BY cls.relname", $this->oid));
if (!$result) {
throw new SQLException("Could not list indexes keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
}
while($row = pg_fetch_assoc($result)) {
$name = $row["idxname"];
$unique = ($row["indisunique"] == 't') ? true : false;
if (!isset($this->indexes[$name])) {
$this->indexes[$name] = new IndexInfo($name, $unique);
}
$arrColumns = explode (' ', $row['indkey']);
foreach ($arrColumns as $intColNum)
{
$result2 = pg_query ($this->conn->getResource(), sprintf ("SELECT a.attname
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
WHERE c.oid = '%s' AND a.attnum = %d AND NOT a.attisdropped
ORDER BY a.attnum", $this->oid, $intColNum));
if (!$result2)
{
throw new SQLException("Could not list indexes keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
}
$row2 = pg_fetch_assoc($result2);
$this->indexes[$name]->addColumn($this->columns[ $row2['attname'] ]);
} // foreach ($arrColumns as $intColNum)
}
$this->indexesLoaded = true;
}
/** Loads the primary keys for this table. */
protected function initPrimaryKey() {
include_once 'creole/metadata/PrimaryKeyInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
// Primary Keys
$result = pg_query($this->conn->getResource(), sprintf ("SELECT
DISTINCT ON(cls.relname)
cls.relname as idxname,
indkey,
indisunique
FROM pg_index idx
JOIN pg_class cls ON cls.oid=indexrelid
WHERE indrelid = %s AND indisprimary
ORDER BY cls.relname", $this->oid));
if (!$result) {
throw new SQLException("Could not list primary keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
}
// Loop through the returned results, grouping the same key_name together
// adding each column for that key.
while($row = pg_fetch_assoc($result)) {
$arrColumns = explode (' ', $row['indkey']);
foreach ($arrColumns as $intColNum)
{
$result2 = pg_query ($this->conn->getResource(), sprintf ("SELECT a.attname
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
WHERE c.oid = '%s' AND a.attnum = %d AND NOT a.attisdropped
ORDER BY a.attnum", $this->oid, $intColNum));
if (!$result2)
{
throw new SQLException("Could not list indexes keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
}
$row2 = pg_fetch_assoc($result2);
if (!isset($this->primaryKey)) {
$this->primaryKey = new PrimaryKeyInfo($row2['attname']);
}
$this->primaryKey->addColumn($this->columns[ $row2['attname'] ]);
} // foreach ($arrColumns as $intColNum)
}
$this->pkLoaded = true;
}
}

View File

@ -0,0 +1,245 @@
<?php
/*
* $Id: SQLiteConnection.php,v 1.15 2006/01/17 19:44:41 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Connection.php';
require_once 'creole/common/ConnectionCommon.php';
/**
* SQLite implementation of Connection.
*
* @author Hans Lellelid <hans@xmpl.org>
* @author Stig Bakken <ssb@fast.no>
* @author Lukas Smith
* @version $Revision: 1.15 $
* @package creole.drivers.sqlite
*/
class SQLiteConnection extends ConnectionCommon implements Connection {
/**
* The case to use for SQLite results.
* (0=nochange, 1=upper, 2=lower)
* This is set in each call to executeQuery() in order to ensure that different
* Connections do not overwrite each other's settings
*/
private $sqliteAssocCase;
/**
* @see Connection::connect()
*/
function connect($dsninfo, $flags = 0)
{
if (!extension_loaded('sqlite')) {
throw new SQLException('sqlite extension not loaded');
}
$file = $dsninfo['database'];
$this->dsn = $dsninfo;
$this->flags = $flags;
$persistent = ($flags & Creole::PERSISTENT === Creole::PERSISTENT);
if (PHP_VERSION == '5.0.4' || PHP_VERSION == '5.0.5') {
$nochange = TRUE;
} else {
$nochange = !(($flags & Creole::COMPAT_ASSOC_LOWER) === Creole::COMPAT_ASSOC_LOWER);
}
if ($nochange) {
$this->sqliteAssocCase = 0;
} else {
$this->sqliteAssocCase = 2;
}
if ($file === null) {
throw new SQLException("No SQLite database specified.");
}
$mode = (isset($dsninfo['mode']) && is_numeric($dsninfo['mode'])) ? $dsninfo['mode'] : 0644;
if ($file != ':memory:') {
if (!file_exists($file)) {
touch($file);
chmod($file, $mode);
if (!file_exists($file)) {
throw new SQLException("Unable to create SQLite database.");
}
}
if (!is_file($file)) {
throw new SQLException("Unable to open SQLite database: not a valid file.");
}
if (!is_readable($file)) {
throw new SQLException("Unable to read SQLite database.");
}
}
$connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open';
if (!($conn = @$connect_function($file, $mode, $errmsg) )) {
throw new SQLException("Unable to connect to SQLite database", $errmsg);
}
$this->dblink = $conn;
}
/**
* @see Connection::getDatabaseInfo()
*/
public function getDatabaseInfo()
{
require_once 'creole/drivers/sqlite/metadata/SQLiteDatabaseInfo.php';
return new SQLiteDatabaseInfo($this);
}
/**
* @see Connection::getIdGenerator()
*/
public function getIdGenerator()
{
require_once 'creole/drivers/sqlite/SQLiteIdGenerator.php';
return new SQLiteIdGenerator($this);
}
/**
* @see Connection::prepareStatement()
*/
public function prepareStatement($sql)
{
require_once 'creole/drivers/sqlite/SQLitePreparedStatement.php';
return new SQLitePreparedStatement($this, $sql);
}
/**
* @see Connection::prepareCall()
*/
public function prepareCall($sql) {
throw new SQLException('SQLite does not support stored procedures using CallableStatement.');
}
/**
* @see Connection::createStatement()
*/
public function createStatement()
{
require_once 'creole/drivers/sqlite/SQLiteStatement.php';
return new SQLiteStatement($this);
}
/**
* @see Connection::close()
*/
function close()
{
$ret = @sqlite_close($this->dblink);
$this->dblink = null;
return $ret;
}
/**
* @see Connection::applyLimit()
*/
public function applyLimit(&$sql, $offset, $limit)
{
if ( $limit > 0 ) {
$sql .= " LIMIT " . $limit . ($offset > 0 ? " OFFSET " . $offset : "");
} elseif ( $offset > 0 ) {
$sql .= " LIMIT -1 OFFSET " . $offset;
}
}
/**
* @see Connection::executeQuery()
*/
public function executeQuery($sql, $fetchmode = null)
{
ini_set('sqlite.assoc_case', $this->sqliteAssocCase);
$this->lastQuery = $sql;
$result = @sqlite_query($this->dblink, $this->lastQuery);
if (!$result) {
throw new SQLException('Could not execute query', $php_errormsg, $this->lastQuery); //sqlite_error_string(sqlite_last_error($this->dblink))
}
require_once 'creole/drivers/sqlite/SQLiteResultSet.php';
return new SQLiteResultSet($this, $result, $fetchmode);
}
/**
* @see Connection::executeUpdate()
*/
function executeUpdate($sql)
{
$this->lastQuery = $sql;
$result = @sqlite_query($this->dblink, $this->lastQuery);
if (!$result) {
throw new SQLException('Could not execute update', $php_errormsg, $this->lastQuery); //sqlite_error_string(sqlite_last_error($this->dblink))
}
return (int) @sqlite_changes($this->dblink);
}
/**
* Start a database transaction.
* @throws SQLException
* @return void
*/
protected function beginTrans()
{
$result = @sqlite_query($this->dblink, 'BEGIN');
if (!$result) {
throw new SQLException('Could not begin transaction', $php_errormsg); //sqlite_error_string(sqlite_last_error($this->dblink))
}
}
/**
* Commit the current transaction.
* @throws SQLException
* @return void
*/
protected function commitTrans()
{
$result = @sqlite_query($this->dblink, 'COMMIT');
if (!$result) {
throw new SQLException('Can not commit transaction', $php_errormsg); // sqlite_error_string(sqlite_last_error($this->dblink))
}
}
/**
* Roll back (undo) the current transaction.
* @throws SQLException
* @return void
*/
protected function rollbackTrans()
{
$result = @sqlite_query($this->dblink, 'ROLLBACK');
if (!$result) {
throw new SQLException('Could not rollback transaction', $php_errormsg); // sqlite_error_string(sqlite_last_error($this->dblink))
}
}
/**
* Gets the number of rows affected by the data manipulation
* query.
*
* @return int Number of rows affected by the last query.
*/
function getUpdateCount()
{
return (int) @sqlite_changes($this->dblink);
}
}

View File

@ -0,0 +1,60 @@
<?php
require_once 'creole/IdGenerator.php';
/**
* SQLite IdGenerator implimenation.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.4 $
* @package creole.drivers.sqlite
*/
class SQLiteIdGenerator implements IdGenerator {
/** Connection object that instantiated this class */
private $conn;
/**
* Creates a new IdGenerator class, saves passed connection for use
* later by getId() method.
* @param Connection $conn
*/
public function __construct(Connection $conn)
{
$this->conn = $conn;
}
/**
* @see IdGenerator::isBeforeInsert()
*/
public function isBeforeInsert()
{
return false;
}
/**
* @see IdGenerator::isAfterInsert()
*/
public function isAfterInsert()
{
return true;
}
/**
* @see IdGenerator::getIdMethod()
*/
public function getIdMethod()
{
return self::AUTOINCREMENT;
}
/**
* @see IdGenerator::getId()
*/
public function getId($unused = null)
{
return sqlite_last_insert_rowid($this->conn->getResource());
}
}

View File

@ -0,0 +1,61 @@
<?php
/*
* $Id: SQLitePreparedStatement.php,v 1.7 2004/03/20 04:16:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/PreparedStatement.php';
require_once 'creole/common/PreparedStatementCommon.php';
/**
* MySQL subclass for prepared statements.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.7 $
* @package creole.drivers.sqlite
*/
class SQLitePreparedStatement extends PreparedStatementCommon implements PreparedStatement {
/**
* Quotes string using native sqlite_escape_string() function.
* @see ResultSetCommon::escape()
*/
protected function escape($str)
{
return sqlite_escape_string($str);
}
/**
* Applies sqlite_udf_encode_binary() to ensure that binary contents will be handled correctly by sqlite.
* @see PreparedStatement::setBlob()
* @see ResultSet::getBlob()
*/
function setBlob($paramIndex, $blob)
{
if ($blob === null) {
$this->setNull($paramIndex);
} else {
// they took magic __toString() out of PHP5.0.0; this sucks
if (is_object($blob)) {
$blob = $blob->__toString();
}
$this->boundInVars[$paramIndex] = "'" . sqlite_udf_encode_binary( $blob ) . "'";
}
}
}

View File

@ -0,0 +1,120 @@
<?php
/*
* $Id: SQLiteResultSet.php,v 1.9 2004/11/29 13:41:24 micha Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/ResultSet.php';
require_once 'creole/common/ResultSetCommon.php';
/**
* SQLite implementation of ResultSet class.
*
* SQLite supports OFFSET / LIMIT natively; this means that no adjustments or checking
* are performed. We will assume that if the lmitSQL() operation failed that an
* exception was thrown, and that OFFSET/LIMIT will never be emulated for SQLite.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.9 $
* @package creole.drivers.sqlite
*/
class SQLiteResultSet extends ResultSetCommon implements ResultSet {
/**
* Gets optimized SQLiteResultSetIterator.
* @return SQLiteResultSetIterator
*/
public function getIterator()
{
require_once 'creole/drivers/sqlite/SQLiteResultSetIterator.php';
return new SQLiteResultSetIterator($this);
}
/**
* @see ResultSet::seek()
*/
public function seek($rownum)
{
// MySQL rows start w/ 0, but this works, because we are
// looking to move the position _before_ the next desired position
if (!@sqlite_seek($this->result, $rownum)) {
return false;
}
$this->cursorPos = $rownum;
return true;
}
/**
* @see ResultSet::next()
*/
function next()
{
$this->fields = sqlite_fetch_array($this->result, $this->fetchmode); // (ResultSet::FETCHMODE_NUM = SQLITE_NUM, etc.)
if (!$this->fields) {
$errno = sqlite_last_error($this->conn->getResource());
if (!$errno) {
// We've advanced beyond end of recordset.
$this->afterLast();
return false;
} else {
throw new SQLException("Error fetching result", sqlite_error_string($errno));
}
}
// Advance cursor position
$this->cursorPos++;
return true;
}
/**
* @see ResultSet::getRecordCount()
*/
public function getRecordCount()
{
$rows = @sqlite_num_rows($this->result);
if ($rows === null) {
throw new SQLException("Error fetching num rows", sqlite_error_string(sqlite_last_error($this->conn->getResource())));
}
return (int) $rows;
}
/**
* Performs sqlite_udf_decode_binary on binary data.
* @see ResultSet::getBlob()
*/
public function getBlob($column)
{
$idx = (is_int($column) ? $column - 1 : $column);
if (!array_key_exists($idx, $this->fields)) { throw new SQLException("Invalid resultset column: " . $column); }
if ($this->fields[$idx] === null) { return null; }
require_once 'creole/util/Blob.php';
$b = new Blob();
$b->setContents(sqlite_udf_decode_binary($this->fields[$idx]));
return $b;
}
/**
* Simply empties array as there is no result free method for sqlite.
* @see ResultSet::close()
*/
public function close()
{
$this->fields = array();
$this->result = null;
}
}

View File

@ -0,0 +1,88 @@
<?php
/*
* $Id: SQLiteResultSetIterator.php,v 1.6 2004/12/03 16:57:54 gamr Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Optimized iterator for SQLite.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.6 $
* @package creole.drivers.sqlite
*/
class SQLiteResultSetIterator implements Iterator {
private $result;
private $pos = 0;
private $fetchmode;
private $row_count;
/**
* Construct the iterator.
* @param SQLiteResultSet $rs
*/
public function __construct(SQLiteResultSet $rs)
{
$this->result = $rs->getResource();
$this->fetchmode = $rs->getFetchmode();
$this->row_count = $rs->getRecordCount();
}
/**
* This method actually has no effect, since we do not rewind ResultSet for iteration.
*/
function rewind()
{
sqlite_rewind($this->result);
}
function valid()
{
return ( $this->pos < $this->row_count );
}
/**
* Returns the cursor position. Note that this will not necessarily
* be 1 for the first row, since no rewind is performed at beginning
* of iteration.
* @return int
*/
function key()
{
return $this->pos;
}
/**
* Returns the row (assoc array) at current cursor pos.
* @return array
*/
function current()
{
return sqlite_fetch_array($this->result, $this->fetchmode);
}
/**
* Advances internal cursor pos.
*/
function next()
{
$this->pos++;
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
* $Id: SQLiteStatement.php,v 1.1 2004/02/19 02:49:43 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/Statement.php';
require_once 'creole/common/StatementCommon.php';
/**
* SQLite Statement
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.1 $
* @package creole.drivers.sqlite
*/
class SQLiteStatement extends StatementCommon implements Statement {
}

View File

@ -0,0 +1,108 @@
<?php
/*
* $Id: SQLiteTypes.php,v 1.3 2004/03/20 04:16:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/CreoleTypes.php';
/**
* MySQL types / type map.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.3 $
* @package creole.drivers.sqlite
*/
class SQLiteTypes extends CreoleTypes {
/**
* Map some fake SQLite types CreoleTypes.
* SQLite is typeless so this is really only for "hint" / readability
* purposes.
* @var array
*/
private static $typeMap = array(
'tinyint' => CreoleTypes::TINYINT,
'smallint' => CreoleTypes::SMALLINT,
'mediumint' => CreoleTypes::SMALLINT,
'int' => CreoleTypes::INTEGER,
'integer' => CreoleTypes::INTEGER,
'bigint' => CreoleTypes::BIGINT,
'int24' => CreoleTypes::BIGINT,
'real' => CreoleTypes::REAL,
'float' => CreoleTypes::FLOAT,
'decimal' => CreoleTypes::DECIMAL,
'numeric' => CreoleTypes::NUMERIC,
'double' => CreoleTypes::DOUBLE,
'char' => CreoleTypes::CHAR,
'varchar' => CreoleTypes::VARCHAR,
'date' => CreoleTypes::DATE,
'time' => CreoleTypes::TIME,
'year' => CreoleTypes::YEAR,
'datetime' => CreoleTypes::TIMESTAMP,
'timestamp' => CreoleTypes::TIMESTAMP,
'tinyblob' => CreoleTypes::BINARY,
'blob' => CreoleTypes::VARBINARY,
'mediumblob' => CreoleTypes::VARBINARY,
'longblob' => CreoleTypes::VARBINARY,
'tinytext' => CreoleTypes::VARCHAR,
'mediumtext' => CreoleTypes::LONGVARCHAR,
'text' => CreoleTypes::LONGVARCHAR,
);
/** Reverse mapping, created on demand. */
private static $reverseMap = null;
/**
* This method returns the generic Creole (JDBC-like) type
* when given the native db type. If no match is found then we just
* return CreoleTypes::TEXT because SQLite is typeless.
* @param string $nativeType DB native type (e.g. 'TEXT', 'byetea', etc.).
* @return int Creole native type (e.g. CreoleTypes::LONGVARCHAR, CreoleTypes::BINARY, etc.).
*/
public static function getType($nativeType)
{
$t = strtolower($nativeType);
if (isset(self::$typeMap[$t])) {
return self::$typeMap[$t];
} else {
return CreoleTypes::TEXT; // because SQLite is typeless
}
}
/**
* This method will return a native type that corresponds to the specified
* Creole (JDBC-like) type. Remember that this is really only for "hint" purposes
* as SQLite is typeless.
*
* If there is more than one matching native type, then the LAST defined
* native type will be returned.
*
* @param int $creoleType
* @return string Native type string.
*/
public static function getNativeType($creoleType)
{
if (self::$reverseMap === null) {
self::$reverseMap = array_flip(self::$typeMap);
}
return @self::$reverseMap[$creoleType];
}
}

View File

@ -0,0 +1,64 @@
<?php
/*
* $Id: SQLiteDatabaseInfo.php,v 1.3 2004/03/20 04:16:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/DatabaseInfo.php';
/**
* SQLite implementation of DatabaseInfo.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.3 $
* @package creole.drivers.sqlite.metadata
*/
class SQLiteDatabaseInfo extends DatabaseInfo {
/**
* @throws SQLException
* @return void
*/
protected function initTables()
{
include_once 'creole/drivers/sqlite/metadata/SQLiteTableInfo.php';
$sql = "SELECT name FROM sqlite_master WHERE type='table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='table' ORDER BY name;";
$result = sqlite_query($this->dblink, $sql);
if (!$result) {
throw new SQLException("Could not list tables", sqlite_last_error($this->dblink));
}
while ($row = sqlite_fetch_array($result)) {
$this->tables[strtoupper($row[0])] = new SQLiteTableInfo($this, $row[0]);
}
}
/**
* SQLite does not support sequences.
*
* @return void
* @throws SQLException
*/
protected function initSequences()
{
// throw new SQLException("MySQL does not support sequences natively.");
}
}

View File

@ -0,0 +1,137 @@
<?php
/*
* $Id: SQLiteTableInfo.php,v 1.8 2005/10/18 02:27:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/metadata/TableInfo.php';
/**
* MySQL implementation of TableInfo.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.8 $
* @package creole.drivers.sqlite.metadata
*/
class SQLiteTableInfo extends TableInfo {
/** Loads the columns for this table. */
protected function initColumns()
{
include_once 'creole/metadata/ColumnInfo.php';
include_once 'creole/metadata/PrimaryKeyInfo.php';
include_once 'creole/drivers/sqlite/SQLiteTypes.php';
// To get all of the attributes we need, we'll actually do
// two separate queries. The first gets names and default values
// the second will fill in some more details.
$sql = "PRAGMA table_info('".$this->name."')";
$res = sqlite_query($this->conn->getResource(), $sql);
while($row = sqlite_fetch_array($res, SQLITE_ASSOC)) {
$name = $row['name'];
$fulltype = $row['type'];
$size = null;
$precision = null;
$scale = null;
if (preg_match('/^([^\(]+)\(\s*(\d+)\s*,\s*(\d+)\s*\)$/', $fulltype, $matches)) {
$type = $matches[1];
$precision = $matches[2];
$scale = $matches[3]; // aka precision
} elseif (preg_match('/^([^\(]+)\(\s*(\d+)\s*\)$/', $fulltype, $matches)) {
$type = $matches[1];
$size = $matches[2];
} else {
$type = $fulltype;
}
// If column is primary key and of type INTEGER, it is auto increment
// See: http://sqlite.org/faq.html#q1
$is_auto_increment = ($row['pk'] == 1 && $fulltype == 'INTEGER');
$not_null = $row['notnull'];
$is_nullable = !$not_null;
$default_val = $row['dflt_value'];
$this->columns[$name] = new ColumnInfo($this, $name, SQLiteTypes::getType($type), $type, $size, $precision, $scale, $is_nullable, $default_val);
if (($row['pk'] == 1) || (strtolower($type) == 'integer primary key')) {
if ($this->primaryKey === null) {
$this->primaryKey = new PrimaryKeyInfo($name);
}
$this->primaryKey->addColumn($this->columns[ $name ]);
}
}
$this->colsLoaded = true;
}
/** Loads the primary key information for this table. */
protected function initPrimaryKey()
{
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
// keys are loaded by initColumns() in this class.
$this->pkLoaded = true;
}
/** Loads the indexes for this table. */
protected function initIndexes() {
include_once 'creole/metadata/IndexInfo.php';
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
$sql = "PRAGMA index_list('".$this->name."')";
$res = sqlite_query($this->conn->getResource(), $sql);
while($row = sqlite_fetch_array($res, SQLITE_ASSOC)) {
$name = $row['name'];
$this->indexes[$name] = new IndexInfo($name);
// get columns for that index
$res2 = sqlite_query($this->conn->getResource(), "PRAGMA index_info('$name')");
while($row2 = sqlite_fetch_array($res2, SQLITE_ASSOC)) {
$colname = $row2['name'];
$this->indexes[$name]->addColumn($this->columns[ $colname ]);
}
}
$this->indexesLoaded = true;
}
/** Load foreign keys (unsupported in SQLite). */
protected function initForeignKeys() {
// columns have to be loaded first
if (!$this->colsLoaded) $this->initColumns();
// No fkeys in SQLite
$this->fksLoaded = true;
}
}

View File

@ -0,0 +1,232 @@
<?php
/*
* $Id: ColumnInfo.php,v 1.13 2005/02/25 15:47:02 pachanga Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Represents a Column.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.13 $
* @package creole.metadata
*/
class ColumnInfo {
// FIXME
// - Currently all member attributes are public. This should be fixed
// when PHP's magic __sleep() and __wakeup() functions & serialization support
// handles protected/private members. (if ever)
/** Column name */
public $name;
/** Column Creole type. */
public $type;
/** Column native type */
public $nativeType;
/** Column length */
public $size;
/** Column presision */
public $precision;
/** Column scale (number of digits after decimal ) */
public $scale;
/** Is nullable? */
public $isNullable;
/** Default value */
public $defaultValue;
/** Is autoincrement? */
public $isAutoIncrement;
/** Table */
public $table;
/**
* Additional and optional vendor specific information.
* @var vendorSpecificInfo
*/
protected $vendorSpecificInfo = array();
/**
* Construct a new ColumnInfo object.
*
* @param TableInfo $table The table that owns this column.
* @param string $name Column name.
* @param int $type Creole type.
* @param string $nativeType Native type name.
* @param int $size Column length.
* @param int $scale Column scale (number of digits after decimal).
* @param boolean $is_nullable Whether col is nullable.
* @param mixed $default Default value.
* @param boolean $is_auto_increment Whether col is of autoIncrement type.
*/
function __construct(TableInfo
$table,
$name,
$type = null,
$nativeType = null,
$size = null,
$precision=null,
$scale = null,
$is_nullable = null,
$default = null,
$is_auto_increment = null,
$vendorInfo = array())
{
$this->table = $table;
$this->name = $name;
$this->type = $type;
$this->nativeType = $nativeType;
$this->size = $size;
$this->precision = $precision;
$this->scale = $scale;
$this->isNullable = $is_nullable;
$this->defaultValue = $default;
$this->isAutoIncrement = $is_auto_increment;
$this->vendorSpecificInfo = $vendorInfo;
}
/**
* This "magic" method is invoked upon serialize().
* Because the Info class hierarchy is recursive, we must handle
* the serialization and unserialization of this object.
* @return array The class variables that should be serialized (all must be public!).
*/
function __sleep()
{
return array('name', 'type', 'nativeType', 'size', 'precision', 'isNullable', 'defaultValue');
}
/**
* Get column name.
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Get column type.
* @return int
*/
public function getType()
{
return $this->type;
}
/**
* Gets the native type name.
* @return string
*/
public function getNativeType()
{
return $this->nativeType;
}
/**
* Get column size.
* @return int
*/
public function getSize()
{
return $this->size;
}
/**
* Get column precision.
* @return int
*/
public function getPrecision()
{
return $this->precision;
}
/**
* Get column scale.
* Scale refers to number of digits after the decimal. Sometimes this is referred
* to as precision, but precision is the total number of digits (i.e. length).
* @return int
*/
public function getScale()
{
return $this->scale;
}
/**
* Get the default value.
* @return mixed
*/
public function getDefaultValue()
{
return $this->defaultValue;
}
/**
* Is column nullable?
* @return boolean
*/
public function isNullable()
{
return $this->isNullable;
}
/**
* Is column of autoincrement type?
* @return boolean
*/
public function isAutoIncrement()
{
return $this->isAutoIncrement === true;
}
/**
* Get vendor specific optional information for this column.
* @return array vendorSpecificInfo[]
*/
public function getVendorSpecificInfo()
{
return $this->vendorSpecificInfo;
}
/**
* @return string
*/
public function toString()
{
return $this->name;
}
/**
* Get parent table.
* @return TableInfo
*/
public function getTable()
{
return $this->table;
}
}

View File

@ -0,0 +1,207 @@
<?php
/*
* $Id: DatabaseInfo.php,v 1.15 2005/11/08 04:24:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* "Info" metadata class for a database.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.15 $
* @package creole.metadata
*/
abstract class DatabaseInfo {
protected $tables = array();
protected $sequences = array();
/** have tables been loaded */
protected $tablesLoaded = false;
/** have sequences been loaded */
protected $seqsLoaded = false;
/** additional vendor specific information */
private $vendorSpecificInfo = array();
/**
* The database Connection.
* @var Connection
*/
protected $conn;
/** Database name. */
protected $dbname;
/**
* Database link
* @var resource
*/
protected $dblink;
/**
* @param Connection $dbh
*/
public function __construct(Connection $conn, $vendorInfo = array())
{
$this->conn = $conn;
$this->dblink = $conn->getResource();
$dsn = $conn->getDSN();
$this->dbname = $dsn['database'];
$this->vendorSpecificInfo = $vendorInfo;
}
/**
* Get name of database.
* @return string
*/
public function getName()
{
return $this->dbname;
}
/**
* This method is invoked upon serialize().
* Because the Info class hierarchy is recursive, we must handle
* the serialization and unserialization of this object.
* @return array The class variables that should be serialized (all must be public!).
*/
function __sleep()
{
return array('tables','sequences','conn');
}
/**
* This method is invoked upon unserialize().
* This method re-hydrates the object and restores the recursive hierarchy.
*/
function __wakeup()
{
// Re-init vars from serialized connection
$this->dbname = $conn->database;
$this->dblink = $conn->connection;
// restore chaining
foreach($this->tables as $tbl) {
$tbl->database = $this;
$tbl->dbname = $this->dbname;
$tbl->dblink = $this->dblink;
$tbl->schema = $this->schema;
}
}
/**
* Returns Connection being used.
* @return Connection
*/
public function getConnection()
{
return $this->conn;
}
/**
* Get the TableInfo object for specified table name.
* @param string $name The name of the table to retrieve.
* @return TableInfo
* @throws SQLException - if table does not exist in this db.
*/
public function getTable($name)
{
if(!$this->tablesLoaded) $this->initTables();
if (!isset($this->tables[strtoupper($name)])) {
throw new SQLException("Database `".$this->dbname."` has no table `".$name."`");
}
return $this->tables[ strtoupper($name) ];
}
/**
* Return whether database contains specified table.
* @param string $name The table name.
* @return boolean
*/
public function hasTable($name)
{
if(!$this->tablesLoaded) $this->initTables();
return isset($this->tables[strtoupper($name)]);
}
/**
* Gets array of TableInfo objects.
* @return array TableInfo[]
*/
public function getTables()
{
if(!$this->tablesLoaded) $this->initTables();
return array_values($this->tables); //re-key [numerically]
}
/**
* Adds a table to this db.
* Table name is case-insensitive.
* @param TableInfo $table
*/
public function addTable(TableInfo $table)
{
$this->tables[strtoupper($table->getName())] = $table;
}
/**
* @return void
* @throws SQLException
*/
abstract protected function initTables();
/**
* @return void
* @throws SQLException
*/
abstract protected function initSequences();
/**
* @return boolean
* @throws SQLException
*/
public function isSequence($key)
{
if(!$this->seqsLoaded) $this->initSequences();
return isset($this->sequences[ strtoupper($key) ]);
}
/**
* Gets array of ? objects.
* @return array ?[]
*/
public function getSequences()
{
if(!$this->seqsLoaded) $this->initSequences();
return array_values($this->sequences); //re-key [numerically]
}
/**
* Get vendor specific optional information for this primary key.
* @return array vendorSpecificInfo[]
*/
public function getVendorSpecificInfo()
{
return $this->vendorSpecificInfo;
}
}

View File

@ -0,0 +1,103 @@
<?php
/*
* $Id: ForeignKeyInfo.php,v 1.9 2005/08/02 14:42:36 sethr Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Represents a foreign key.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.9 $
* @package creole.metadata
*/
class ForeignKeyInfo {
private $name;
private $references = array();
/**
* Additional and optional vendor specific information.
* @var vendorSpecificInfo
*/
protected $vendorSpecificInfo = array();
const NONE = ""; // No "ON [ DELETE | UPDATE]" behaviour specified.
const NOACTION = "NO ACTION";
const CASCADE = "CASCADE";
const RESTRICT = "RESTRICT";
const SETDEFAULT = "SET DEFAULT";
const SETNULL = "SET NULL";
/**
* @param string $name The name of the foreign key.
*/
function __construct($name, $vendorInfo = array())
{
$this->name = $name;
$this->vendorSpecificInfo = $vendorInfo;
}
/**
* Get foreign key name.
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Adds a foreign-local mapping.
* @param ColumnInfo $local
* @param ColumnInfo $foreign
*/
public function addReference(ColumnInfo $local, ColumnInfo $foreign, $onDelete = self::NONE, $onUpdate = self::NONE)
{
$this->references[] = array($local, $foreign, $onDelete, $onUpdate);
}
/**
* Gets the local-foreign column mapping.
* @return array array( [0] => array([0] => local ColumnInfo object, [1] => foreign ColumnInfo object, [2] => onDelete, [3] => onUpdate) )
*/
public function getReferences()
{
return $this->references;
}
/**
* Get vendor specific optional information for this primary key.
* @return array vendorSpecificInfo[]
*/
public function getVendorSpecificInfo()
{
return $this->vendorSpecificInfo;
}
/**
* @return string
*/
public function toString()
{
return $this->name;
}
}

View File

@ -0,0 +1,84 @@
<?php
/*
* $Id: IndexInfo.php,v 1.7 2005/02/25 15:47:02 pachanga Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Represents an index.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.7 $
* @package creole.metadata
*/
class IndexInfo {
/** name of the index */
private $name;
/** columns in this index */
private $columns = array();
/** uniqueness flag */
private $isUnique = false;
/** additional vendor specific information */
private $vendorSpecificInfo = array();
function __construct($name, $isUnique = false, $vendorInfo = array())
{
$this->name = $name;
$this->isUnique = $isUnique;
$this->vendorSpecificInfo = $vendorInfo;
}
public function isUnique()
{
return $this->isUnique;
}
public function getName()
{
return $this->name;
}
/**
* Get vendor specific optional information for this index.
* @return array vendorSpecificInfo[]
*/
public function getVendorSpecificInfo()
{
return $this->vendorSpecificInfo;
}
public function addColumn($column)
{
$this->columns[] = $column;
}
public function getColumns()
{
return $this->columns;
}
public function toString()
{
return $this->name;
}
}

View File

@ -0,0 +1,91 @@
<?php
/*
* $Id: PrimaryKeyInfo.php,v 1.6 2005/02/25 15:47:02 pachanga Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Represents a PrimaryKey
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.6 $
* @package creole.metadata
*/
class PrimaryKeyInfo {
/** name of the primary key */
private $name;
/** columns in the primary key */
private $columns = array();
/** additional vendor specific information */
private $vendorSpecificInfo = array();
/**
* @param string $name The name of the foreign key.
*/
function __construct($name, $vendorInfo = array())
{
$this->name = $name;
$this->vendorSpecificInfo = $vendorInfo;
}
/**
* Get foreign key name.
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param Column $column
* @return void
*/
public function addColumn($column)
{
$this->columns[] = $column;
}
/**
* @return array Column[]
*/
public function getColumns()
{
return $this->columns;
}
/**
* Get vendor specific optional information for this primary key.
* @return array vendorSpecificInfo[]
*/
public function getVendorSpecificInfo()
{
return $this->vendorSpecificInfo;
}
/**
* @return string
*/
public function toString()
{
return $this->name;
}
}

View File

@ -0,0 +1,305 @@
<?php
/*
* $Id: TableInfo.php,v 1.16 2005/10/17 19:05:10 dlawson_mi Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Represents a table.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.16 $
* @package creole.metadata
*/
abstract class TableInfo {
protected $name;
protected $columns = array();
protected $foreignKeys = array();
protected $indexes = array();
protected $primaryKey;
protected $pkLoaded = false;
protected $fksLoaded = false;
protected $indexesLoaded = false;
protected $colsLoaded = false;
protected $vendorLoaded = false;
/**
* Additional and optional vendor specific information.
* @var vendorSpecificInfo
*/
protected $vendorSpecificInfo = array();
/**
* Database Connection.
* @var Connection
*/
protected $conn;
/**
* The parent DatabaseInfo object.
* @var DatabaseInfo
*/
protected $database;
/** Shortcut to db resource link id (needed by drivers for queries). */
protected $dblink;
/** Shortcut to db name (needed by many drivers for queries). */
protected $dbname;
/**
* @param string $table The table name.
* @param string $database The database name.
* @param resource $dblink The db connection resource.
*/
function __construct(DatabaseInfo $database, $name) {
$this->database = $database;
$this->name = $name;
$this->conn = $database->getConnection(); // shortcut because all drivers need this for the queries
$this->dblink = $this->conn->getResource();
$this->dbname = $database->getName();
}
/**
* This "magic" method is invoked upon serialize().
* Because the Info class hierarchy is recursive, we must handle
* the serialization and unserialization of this object.
* @return array The class variables that should be serialized (all must be public!).
*/
function __sleep()
{
return array('name', 'columns', 'foreignKeys', 'indexes', 'primaryKey');
}
/**
* This "magic" method is invoked upon unserialize().
* This method re-hydrates the object and restores the recursive hierarchy.
*/
function __wakeup()
{
// restore chaining
foreach($this->columns as $col) {
$col->table = $this;
}
}
/**
* Loads the columns.
* @return void
*/
abstract protected function initColumns();
/**
* Loads the primary key information for this table.
* @return void
*/
abstract protected function initPrimaryKey();
/**
* Loads the foreign keys for this table.
* @return void
*/
abstract protected function initForeignKeys();
/**
* Loads the indexes information for this table.
* @return void
*/
abstract protected function initIndexes();
/**
* Loads the vendor specific information for this table.
* @return void
*/
//it must be asbtract and be implemented in every vendor specific driver,
//however since it's an experimental stuff it has an empty body in order
//not to break BC
/*abstract*/ protected function initVendorSpecificInfo(){}
/**
* Get parimary key in this table.
* @throws Exception - if foreign keys are unsupported by DB.
* @return array ForeignKeyInfo[]
*/
public function getPrimaryKey()
{
if(!$this->pkLoaded) $this->initPrimaryKey();
return $this->primaryKey;
}
/**
* Get the ColumnInfo object for specified column.
* @param string $name The column name.
* @return ColumnInfo
* @throws SQLException - if column does not exist for this table.
*/
public function getColumn($name)
{
if(!$this->colsLoaded) $this->initColumns();
if (!isset($this->columns[$name])) {
throw new SQLException("Table `".$this->name."` has no column `".$name."`");
}
return $this->columns[$name];
}
/**
* Return whether table contains specified column.
* @param string $name The column name.
* @return boolean
*/
public function hasColumn($name)
{
if(!$this->colsLoaded) $this->initColumns();
return isset($this->columns[$name]);
}
/**
* Get array of columns for this table.
* @return array ColumnInfo[]
*/
public function getColumns()
{
if(!$this->colsLoaded) $this->initColumns();
return array_values($this->columns); // re-key numerically
}
/**
* Get specified fk for this table.
* @param string $name The foreign key name to retrieve.
* @return ForeignKeyInfo
* @throws SQLException - if fkey does not exist for this table.
*/
public function getForeignKey($name)
{
if(!$this->fksLoaded) $this->initForeignKeys();
if (!isset($this->foreignKeys[$name])) {
throw new SQLException("Table `".$this->name."` has no foreign key `".$name."`");
}
return $this->foreignKeys[$name];
}
/**
* Get all foreign keys.
* @return array ForeignKeyInfo[]
*/
public function getForeignKeys()
{
if(!$this->fksLoaded) $this->initForeignKeys();
return array_values($this->foreignKeys);
}
/**
* Gets the IndexInfo object for a specified index.
* @param string $name The index name to retrieve.
* @return IndexInfo
* @throws SQLException - if index does not exist for this table.
*/
public function getIndex($name)
{
if(!$this->indexesLoaded) $this->initIndexes();
if (!isset($this->indexes[$name])) {
throw new SQLException("Table `".$this->name."` has no index `".$name."`");
}
return $this->indexes[$name];
}
/**
* Get array of IndexInfo objects for this table.
* @return array IndexInfo[]
*/
public function getIndexes()
{
if(!$this->indexesLoaded) $this->initIndexes();
return array_values($this->indexes);
}
/**
* Alias for getIndexes() method.
* @return array
*/
public function getIndices()
{
return $this->getIndexes();
}
/**
* Get table name.
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return string
*/
public function toString()
{
return $this->name;
}
/** Have foreign keys been loaded? */
public function foreignKeysLoaded()
{
return $this->fksLoaded;
}
/** Has primary key info been loaded? */
public function primaryKeyLoaded()
{
return $this->pkLoaded;
}
/** Have columns been loaded? */
public function columnsLoaded()
{
return $this->colsLoaded;
}
/** Has index information been loaded? */
public function indexesLoaded()
{
return $this->indexesLoaded;
}
/**
* Get vendor specific optional information for this table.
* @return array vendorSpecificInfo[]
*/
public function getVendorSpecificInfo()
{
if(!$this->vendorLoaded) $this->initVendorSpecificInfo();
return $this->vendorSpecificInfo;
}
/** Adds a column to this table. */
public function addColumn(ColumnInfo $column)
{
$this->columns[$column->getName()] = $column;
}
/** Get the parent DatabaseInfo object. */
public function getDatabase()
{
return $this->database;
}
}

62
lib/symfony/vendor/creole/util/Blob.php vendored Executable file
View File

@ -0,0 +1,62 @@
<?php
/*
* $Id: Blob.php,v 1.5 2004/03/20 04:16:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/util/Lob.php';
/**
* A class for handling binary LOBs.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.5 $
* @package creole.util
*/
class Blob extends Lob {
/**
* Dump the contents of the file using fpassthru().
*
* @return void
* @throws Exception if no file or contents.
*/
function dump()
{
if (!$this->data) {
// hmmm .. must be a file that needs to read in
if ($this->inFile) {
$fp = @fopen($this->inFile, "rb");
if (!$fp) {
throw new Exception('Unable to open file: '.$this->inFile);
}
fpassthru($fp);
@fclose($fp);
} else {
throw new Exception('No data to dump');
}
} else {
echo $this->data;
}
}
}

112
lib/symfony/vendor/creole/util/Clob.php vendored Executable file
View File

@ -0,0 +1,112 @@
<?php
/*
* $Id: Clob.php,v 1.6 2004/07/27 23:15:13 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
require_once 'creole/util/Lob.php';
/**
* A class for handling character (ASCII) LOBs.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.6 $
* @package creole.util
*/
class Clob extends Lob {
/**
* Read LOB data from file.
* @param string $file Filename may also be specified here (if not specified using setInputFile()).
* @return void
* @throws Exception - if no file specified or error on read.
* @see setInputFile()
*/
public function readFromFile($file = null)
{
if ($file !== null) {
$this->setInputFile($file);
}
if (!$this->inFile) {
throw Exception('No file specified for read.');
}
$data = null;
$file = fopen($this->inFile, "rt");
while (!feof($file)) $data .= fgets($file, 4096);
fclose($file);
if ($data === false) {
throw new Exception('Unable to read from file: '.$this->inFile);
}
$this->setContents($data);
}
/**
* Write LOB data to file.
* @param string $file Filename may also be specified here (if not set using setOutputFile()).
* @throws Exception - if no file specified, no contents to write, or error on write.
* @see setOutputFile()
*/
public function writeToFile($file = null)
{
if ($file !== null) {
$this->setOutputFile($file);
}
if (!$this->outFile) {
throw new Exception('No file specified for write');
}
if ($this->data === null) {
throw new Exception('No data to write to file');
}
$file = fopen($this->inFile, "wt");
if (fputs($file, $this->data) === false)
throw new Exception('Unable to write to file: '.$this->outFile);
fclose($file);
}
/**
* Dump the contents of the file using fpassthru().
*
* @return void
* @throws Exception if no file or contents.
*/
function dump()
{
if (!$this->data) {
// is there a file name set?
if ($this->inFile) {
$fp = @fopen($this->inFile, "r");
if (!$fp) {
throw new Exception('Unable to open file: '.$this->inFile);
}
fpassthru($fp);
@fclose($fp);
} else {
throw new Exception('No data to dump');
}
} else {
echo $this->data;
}
}
}

243
lib/symfony/vendor/creole/util/Lob.php vendored Executable file
View File

@ -0,0 +1,243 @@
<?php
/*
* $Id: Lob.php,v 1.10 2004/03/20 04:16:50 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* An abstract class for handling LOB (Locator Object) columns.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.10 $
* @package creole.util
*/
abstract class Lob {
/**
* The contents of the Lob.
* DO NOT SET DIRECTLY (or you will disrupt the
* ability of isModified() to give accurate results).
* @var string
*/
protected $data;
/**
* File that blob should be written out to.
* @var string
*/
protected $outFile;
/**
* File that blob should be read in from
* @var string
*/
protected $inFile;
/**
* This is a 3-state value indicating whether column has been
* modified.
* Initially it is NULL. Once first call to setContents() is made
* it is FALSE, because this will be initial state of Lob. Once
* a subsequent call to setContents() is made it is TRUE.
* @var boolean
*/
private $modified = null;
/**
* Construct a new Lob.
* @param sttring $data The data contents of the Lob.
* @see setContents()
*/
public function __construct($data = null)
{
if ($data !== null) {
$this->setContents($data);
}
}
/**
* Get the contents of the LOB.
* @return string The characters in this LOB.
* @throws Exception
*/
public function getContents()
{
if ($this->data === null && $this->isFromFile()) {
$this->readFromFile();
}
return $this->data;
}
/**
* Set the contents of this LOB.
* Sets the modified flag to FALSE if this is the first call
* to setContents() for this object. Sets the bit to TRUE if
* this any subsequent call to setContents().
* @param string $bytes
*/
public function setContents($data)
{
$this->data = $data;
if ($this->modified === null) {
// if modified bit hasn't been set yet,
// then it should now be set to FALSE, since
// we just did inital population
$this->modified = false;
} elseif ($this->modified === false) {
// if it was already FALSE, then it should
// now be set to TRUE, since this is a subsequent
// modfiication.
$this->modified = true;
}
}
/**
* Dump the contents of the file to stdout.
* Must be implemented by subclasses so that binary status is handled
* correctly. (i.e. ignored for Clob, handled for Blob)
* @return void
* @throws Exception if no file or contents.
*/
abstract public function dump();
/**
* Specify the file that we want this LOB read from.
* @param string $filePath The location of the file.
* @return void
*/
public function setInputFile($filePath)
{
$this->inFile = $filePath;
}
/**
* Get the file that we want this LOB read from.
* @return string The location of the file.
*/
public function getInputFile()
{
return $this->inFile;
}
/**
* Specify the file that we want this LOB saved to.
* @param string $filePath The location of the file.
* @return void
*/
public function setOutputFile($filePath)
{
$this->outFile = $filePath;
}
/**
* Get the file that we want this LOB saved to.
* @return string $filePath The location of the file.
*/
public function getOutputFile()
{
return $this->outFile;
}
/**
* Returns whether this Lob is loaded from file.
* This is useful for bypassing need to read in the contents of the Lob.
* @return boolean Whether this LOB is to be read from a file.
*/
public function isFromFile()
{
return ($this->inFile !== null);
}
/**
* Read LOB data from file (binary safe).
* (Implementation may need to be moved into Clob / Blob subclasses, but
* since file_get_contents() is binary-safe, it hasn't been necessary so far.)
* @param string $file Filename may also be specified here (if not specified using setInputFile()).
* @return void
* @throws Exception - if no file specified or error on read.
* @see setInputFile()
*/
public function readFromFile($file = null)
{
if ($file !== null) {
$this->setInputFile($file);
}
if (!$this->inFile) {
throw Exception('No file specified for read.');
}
$data = @file_get_contents($this->inFile);
if ($data === false) {
throw new Exception('Unable to read from file: '.$this->inFile);
}
$this->setContents($data);
}
/**
* Write LOB data to file (binary safe).
* (Impl may need to move into subclasses, but so far not necessary.)
* @param string $file Filename may also be specified here (if not set using setOutputFile()).
* @throws Exception - if no file specified, no contents to write, or error on write.
* @see setOutputFile()
*/
public function writeToFile($file = null)
{
if ($file !== null) {
$this->setOutputFile($file);
}
if (!$this->outFile) {
throw new Exception('No file specified for write');
}
if ($this->data === null) {
throw new Exception('No data to write to file');
}
if (false === @file_put_contents($this->outFile, $this->data)) {
throw new Exception('Unable to write to file: '.$this->outFile);
}
}
/**
* Convenience method to get contents of LOB as string.
* @return string
*/
public function __toString()
{
return $this->getContents();
}
/**
* Set whether LOB contents have been modified after initial setting.
* @param boolean $b
*/
public function setModified($b)
{
$this->modified = $b;
}
/**
* Whether LOB contents have been modified after initial setting.
* @return boolean TRUE if the contents have been modified after initial setting.
* FALSE if contents have not been modified or if no contents have bene set.
*/
public function isModified()
{
// cast it so that NULL will also eval to false
return (boolean) $this->modified;
}
}

View File

@ -0,0 +1,164 @@
<?php
/*
* $Id: SQLStatementExtractor.php,v 1.5 2004/07/27 23:13:46 hlellelid Exp $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Static class for extracting SQL statements from a string or file.
*
* @author Hans Lellelid <hans@xmpl.org>
* @version $Revision: 1.5 $
* @package creole.util.sql
*/
class SQLStatementExtractor {
protected static $delimiter = ';';
/**
* Get SQL statements from file.
*
* @param string $filename Path to file to read.
* @return array SQL statements
*/
public static function extractFile($filename) {
$buffer = file_get_contents($filename);
if ($buffer === false) {
throw new Exception("Unable to read file: " . $filename);
}
return self::extractStatements(self::getLines($buffer));
}
/**
* Extract statements from string.
*
* @param string $txt
* @return array
*/
public static function extract($buffer) {
return self::extractStatements(self::getLines($buffer));
}
/**
* Extract SQL statements from array of lines.
*
* @param array $lines Lines of the read-in file.
* @return string
*/
protected static function extractStatements($lines) {
$statements = array();
$sql = "";
foreach($lines as $line) {
$line = trim($line);
if (self::startsWith("//", $line) ||
self::startsWith("--", $line) ||
self::startsWith("#", $line)) {
continue;
}
if (strlen($line) > 4 && strtoupper(substr($line,0, 4)) == "REM ") {
continue;
}
$sql .= " " . $line;
$sql = trim($sql);
// SQL defines "--" as a comment to EOL
// and in Oracle it may contain a hint
// so we cannot just remove it, instead we must end it
if (strpos($line, "--") !== false) {
$sql .= "\n";
}
if (self::endsWith(self::$delimiter, $sql)) {
$statements[] = self::substring($sql, 0, strlen($sql)-1 - strlen(self::$delimiter));
$sql = "";
}
}
return $statements;
}
//
// Some string helper methods
//
/**
* Tests if a string starts with a given string.
* @param string $check The substring to check.
* @param string $string The string to check in (haystack).
* @return boolean True if $string starts with $check, or they are equal, or $check is empty.
*/
protected static function startsWith($check, $string) {
if ($check === "" || $check === $string) {
return true;
} else {
return (strpos($string, $check) === 0) ? true : false;
}
}
/**
* Tests if a string ends with a given string.
* @param string $check The substring to check.
* @param string $string The string to check in (haystack).
* @return boolean True if $string ends with $check, or they are equal, or $check is empty.
*/
protected static function endsWith($check, $string) {
if ($check === "" || $check === $string) {
return true;
} else {
return (strpos(strrev($string), strrev($check)) === 0) ? true : false;
}
}
/**
* a natural way of getting a subtring, php's circular string buffer and strange
* return values suck if you want to program strict as of C or friends
*/
protected static function substring($string, $startpos, $endpos = -1) {
$len = strlen($string);
$endpos = (int) (($endpos === -1) ? $len-1 : $endpos);
if ($startpos > $len-1 || $startpos < 0) {
trigger_error("substring(), Startindex out of bounds must be 0<n<$len", E_USER_ERROR);
}
if ($endpos > $len-1 || $endpos < $startpos) {
trigger_error("substring(), Endindex out of bounds must be $startpos<n<".($len-1), E_USER_ERROR);
}
if ($startpos === $endpos) {
return (string) $string{$startpos};
} else {
$len = $endpos-$startpos;
}
return substr($string, $startpos, $len+1);
}
/**
* Convert string buffer into array of lines.
*
* @param string $filename
* @return array string[] lines of file.
*/
protected static function getLines($buffer) {
$lines = preg_split("/\r?\n|\r/", $buffer);
return $lines;
}
}

978
lib/symfony/vendor/lime/lime.php vendored Executable file
View File

@ -0,0 +1,978 @@
<?php
/*
* This file is part of the symfony package.
* (c) 2004-2006 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Unit test library.
*
* @package lime
* @author Fabien Potencier <fabien.potencier@gmail.com>
* @version SVN: $Id: lime.php 4259 2007-06-19 07:24:40Z fabien $
*/
class lime_test
{
public $plan = null;
public $test_nb = 0;
public $failed = 0;
public $passed = 0;
public $skipped = 0;
public $output = null;
function __construct($plan = null, $output_instance = null)
{
$this->plan = $plan;
$this->output = $output_instance ? $output_instance : new lime_output();
null !== $this->plan and $this->output->echoln(sprintf("1..%d", $this->plan));
}
function __destruct()
{
$total = $this->passed + $this->failed + $this->skipped;
null === $this->plan and $this->plan = $total and $this->output->echoln(sprintf("1..%d", $this->plan));
if ($total > $this->plan)
{
$this->output->red_bar(sprintf(" Looks like you planned %d tests but ran %d extra.", $this->plan, $total - $this->plan));
}
elseif ($total < $this->plan)
{
$this->output->red_bar(sprintf(" Looks like you planned %d tests but only ran %d.", $this->plan, $total));
}
if ($this->failed)
{
$this->output->red_bar(sprintf(" Looks like you failed %d tests of %d.", $this->failed, $this->plan));
}
else if ($total == $this->plan)
{
$this->output->green_bar(" Looks like everything went fine.");
}
flush();
}
function ok($exp, $message = '')
{
if ($result = (boolean) $exp)
{
++$this->passed;
}
else
{
++$this->failed;
}
$this->output->echoln(sprintf("%s %d%s", $result ? 'ok' : 'not ok', ++$this->test_nb, $message = $message ? sprintf('%s %s', 0 === strpos($message, '#') ? '' : ' -', $message) : ''));
if (!$result)
{
$traces = debug_backtrace();
if ($_SERVER['PHP_SELF'])
{
$i = strstr($traces[0]['file'], $_SERVER['PHP_SELF']) ? 0 : (isset($traces[1]['file']) ? 1 : 0);
}
else
{
$i = 0;
}
$this->output->diag(sprintf(' Failed test (%s at line %d)', str_replace(getcwd(), '.', $traces[$i]['file']), $traces[$i]['line']));
}
return $result;
}
function is($exp1, $exp2, $message = '')
{
if (is_object($exp1) || is_object($exp2))
{
$value = $exp1 === $exp2;
}
else
{
$value = $exp1 == $exp2;
}
if (!$result = $this->ok($value, $message))
{
$this->output->diag(sprintf(" got: %s", str_replace("\n", '', var_export($exp1, true))), sprintf(" expected: %s", str_replace("\n", '', var_export($exp2, true))));
}
return $result;
}
function isnt($exp1, $exp2, $message = '')
{
if (!$result = $this->ok($exp1 != $exp2, $message))
{
$this->output->diag(sprintf(" %s", str_replace("\n", '', var_export($exp1, true))), ' ne', sprintf(" %s", str_replace("\n", '', var_export($exp2, true))));
}
return $result;
}
function like($exp, $regex, $message = '')
{
if (!$result = $this->ok(preg_match($regex, $exp), $message))
{
$this->output->diag(sprintf(" '%s'", $exp), sprintf(" doesn't match '%s'", $regex));
}
return $result;
}
function unlike($exp, $regex, $message = '')
{
if (!$result = $this->ok(!preg_match($regex, $exp), $message))
{
$this->output->diag(sprintf(" '%s'", $exp), sprintf(" matches '%s'", $regex));
}
return $result;
}
function cmp_ok($exp1, $op, $exp2, $message = '')
{
eval(sprintf("\$result = \$exp1 $op \$exp2;"));
if (!$this->ok($result, $message))
{
$this->output->diag(sprintf(" %s", str_replace("\n", '', var_export($exp1, true))), sprintf(" %s", $op), sprintf(" %s", str_replace("\n", '', var_export($exp2, true))));
}
return $result;
}
function can_ok($object, $methods, $message = '')
{
$result = true;
$failed_messages = array();
foreach ((array) $methods as $method)
{
if (!method_exists($object, $method))
{
$failed_messages[] = sprintf(" method '%s' does not exist", $method);
$result = false;
}
}
!$this->ok($result, $message);
!$result and $this->output->diag($failed_messages);
return $result;
}
function isa_ok($var, $class, $message = '')
{
$type = is_object($var) ? get_class($var) : gettype($var);
if (!$result = $this->ok($type == $class, $message))
{
$this->output->diag(sprintf(" isa_ok isn't a '%s' it's a '%s'", $class, $type));
}
return $result;
}
function is_deeply($exp1, $exp2, $message = '')
{
if (!$result = $this->ok($this->test_is_deeply($exp1, $exp2), $message))
{
$this->output->diag(sprintf(" got: %s", str_replace("\n", '', var_export($exp1, true))), sprintf(" expected: %s", str_replace("\n", '', var_export($exp2, true))));
}
return $result;
}
function pass($message = '')
{
return $this->ok(true, $message);
}
function fail($message = '')
{
return $this->ok(false, $message);
}
function diag($message)
{
$this->output->diag($message);
}
function skip($message = '', $nb_tests = 1)
{
for ($i = 0; $i < $nb_tests; $i++)
{
++$this->skipped and --$this->passed;
$this->pass(sprintf("# SKIP%s", $message ? ' '.$message : ''));
}
}
function todo($message = '')
{
++$this->skipped and --$this->passed;
$this->pass(sprintf("# TODO%s", $message ? ' '.$message : ''));
}
function include_ok($file, $message = '')
{
if (!$result = $this->ok((@include($file)) == 1, $message))
{
$this->output->diag(sprintf(" Tried to include '%s'", $file));
}
return $result;
}
private function test_is_deeply($var1, $var2)
{
if (gettype($var1) != gettype($var2))
{
return false;
}
if (is_array($var1))
{
ksort($var1);
ksort($var2);
if (array_diff(array_keys($var1), array_keys($var2)))
{
return false;
}
$is_equal = true;
foreach ($var1 as $key => $value)
{
$is_equal = $this->test_is_deeply($var1[$key], $var2[$key]);
if ($is_equal === false)
{
break;
}
}
return $is_equal;
}
else
{
return $var1 === $var2;
}
}
function comment($message)
{
$this->output->comment($message);
}
static function get_temp_directory()
{
if ('\\' == DIRECTORY_SEPARATOR)
{
foreach (array('TEMP', 'TMP', 'windir') as $dir)
{
if ($var = isset($_ENV[$dir]) ? $_ENV[$dir] : getenv($dir))
{
return $var;
}
}
return getenv('SystemRoot').'\temp';
}
if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR'))
{
return $var;
}
return '/tmp';
}
}
class lime_output
{
function diag()
{
$messages = func_get_args();
foreach ($messages as $message)
{
array_map(array($this, 'comment'), (array) $message);
}
}
function comment($message)
{
echo "# $message\n";
}
function echoln($message)
{
echo "$message\n";
}
function green_bar($message)
{
echo "$message\n";
}
function red_bar($message)
{
echo "$message\n";
}
}
class lime_output_color extends lime_output
{
public $colorizer = null;
function __construct()
{
$this->colorizer = new lime_colorizer();
}
function diag()
{
$messages = func_get_args();
foreach ($messages as $message)
{
echo $this->colorizer->colorize('# '.join("\n# ", (array) $message), 'COMMENT')."\n";
}
}
function comment($message)
{
echo $this->colorizer->colorize(sprintf('# %s', $message), 'COMMENT')."\n";
}
function echoln($message, $colorizer_parameter = null)
{
$message = preg_replace('/(?:^|\.)((?:not ok|dubious) *\d*)\b/e', '$this->colorizer->colorize(\'$1\', \'ERROR\')', $message);
$message = preg_replace('/(?:^|\.)(ok *\d*)\b/e', '$this->colorizer->colorize(\'$1\', \'INFO\')', $message);
$message = preg_replace('/"(.+?)"/e', '$this->colorizer->colorize(\'$1\', \'PARAMETER\')', $message);
$message = preg_replace('/(\->|\:\:)?([a-zA-Z0-9_]+?)\(\)/e', '$this->colorizer->colorize(\'$1$2()\', \'PARAMETER\')', $message);
echo ($colorizer_parameter ? $this->colorizer->colorize($message, $colorizer_parameter) : $message)."\n";
}
function green_bar($message)
{
echo $this->colorizer->colorize($message.str_repeat(' ', 71 - min(71, strlen($message))), 'GREEN_BAR')."\n";
}
function red_bar($message)
{
echo $this->colorizer->colorize($message.str_repeat(' ', 71 - min(71, strlen($message))), 'RED_BAR')."\n";
}
}
class lime_colorizer
{
static public $styles = array();
static function style($name, $options = array())
{
self::$styles[$name] = $options;
}
static function colorize($text = '', $parameters = array())
{
// disable colors if not supported (windows or non tty console)
if (DIRECTORY_SEPARATOR == '\\' || !function_exists('posix_isatty') || !@posix_isatty(STDOUT))
{
return $text;
}
static $options = array('bold' => 1, 'underscore' => 4, 'blink' => 5, 'reverse' => 7, 'conceal' => 8);
static $foreground = array('black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'white' => 37);
static $background = array('black' => 40, 'red' => 41, 'green' => 42, 'yellow' => 43, 'blue' => 44, 'magenta' => 45, 'cyan' => 46, 'white' => 47);
!is_array($parameters) && isset(self::$styles[$parameters]) and $parameters = self::$styles[$parameters];
$codes = array();
isset($parameters['fg']) and $codes[] = $foreground[$parameters['fg']];
isset($parameters['bg']) and $codes[] = $background[$parameters['bg']];
foreach ($options as $option => $value)
{
isset($parameters[$option]) && $parameters[$option] and $codes[] = $value;
}
return "\033[".implode(';', $codes).'m'.$text."\033[0m";
}
}
lime_colorizer::style('ERROR', array('bg' => 'red', 'fg' => 'white', 'bold' => true));
lime_colorizer::style('INFO', array('fg' => 'green', 'bold' => true));
lime_colorizer::style('PARAMETER', array('fg' => 'cyan'));
lime_colorizer::style('COMMENT', array('fg' => 'yellow'));
lime_colorizer::style('GREEN_BAR', array('fg' => 'white', 'bg' => 'green', 'bold' => true));
lime_colorizer::style('RED_BAR', array('fg' => 'white', 'bg' => 'red', 'bold' => true));
class lime_harness extends lime_registration
{
public $php_cli = '';
public $stats = array();
public $output = null;
function __construct($output_instance, $php_cli = null)
{
if (getenv('PHP_PATH'))
{
$this->php_cli = getenv('PHP_PATH');
if (!is_executable($this->php_cli))
{
throw new Exception('The defined PHP_PATH environment variable is not a valid PHP executable.');
}
}
$this->php_cli = null === $php_cli ? PHP_BINDIR.DIRECTORY_SEPARATOR.'php' : $php_cli;
if (!is_executable($this->php_cli))
{
$this->php_cli = $this->find_php_cli();
}
$this->output = $output_instance ? $output_instance : new lime_output();
}
protected function find_php_cli()
{
$path = getenv('PATH') ? getenv('PATH') : getenv('Path');
$exe_suffixes = DIRECTORY_SEPARATOR == '\\' ? (getenv('PATHEXT') ? explode(PATH_SEPARATOR, getenv('PATHEXT')) : array('.exe', '.bat', '.cmd', '.com')) : array('');
foreach (array('php5', 'php') as $php_cli)
{
foreach ($exe_suffixes as $suffix)
{
foreach (explode(PATH_SEPARATOR, $path) as $dir)
{
$file = $dir.DIRECTORY_SEPARATOR.$php_cli.$suffix;
if (is_executable($file))
{
return $file;
}
}
}
}
throw new Exception("Unable to find PHP executable.");
}
function run()
{
if (!count($this->files))
{
throw new Exception('You must register some test files before running them!');
}
// sort the files to be able to predict the order
sort($this->files);
$this->stats =array(
'_failed_files' => array(),
'_failed_tests' => 0,
'_nb_tests' => 0,
);
foreach ($this->files as $file)
{
$this->stats[$file] = array(
'plan' => null,
'nb_tests' => 0,
'failed' => array(),
'passed' => array(),
);
$this->current_file = $file;
$this->current_test = 0;
$relative_file = $this->get_relative_file($file);
ob_start(array($this, 'process_test_output'));
passthru(sprintf('%s -d html_errors=off -d open_basedir= -q "%s" 2>&1', $this->php_cli, $file), $return);
ob_end_clean();
if ($return > 0)
{
$this->stats[$file]['status'] = 'dubious';
$this->stats[$file]['status_code'] = $return;
}
else
{
$delta = $this->stats[$file]['plan'] - $this->stats[$file]['nb_tests'];
if ($delta > 0)
{
$this->output->echoln(sprintf('%s%s%s', substr($relative_file, -min(67, strlen($relative_file))), str_repeat('.', 70 - min(67, strlen($relative_file))), $this->output->colorizer->colorize(sprintf('# Looks like you planned %d tests but only ran %d.', $this->stats[$file]['plan'], $this->stats[$file]['nb_tests']), 'COMMENT')));
$this->stats[$file]['status'] = 'dubious';
$this->stats[$file]['status_code'] = 255;
$this->stats['_nb_tests'] += $delta;
for ($i = 1; $i <= $delta; $i++)
{
$this->stats[$file]['failed'][] = $this->stats[$file]['nb_tests'] + $i;
}
}
else if ($delta < 0)
{
$this->output->echoln(sprintf('%s%s%s', substr($relative_file, -min(67, strlen($relative_file))), str_repeat('.', 70 - min(67, strlen($relative_file))), $this->output->colorizer->colorize(sprintf('# Looks like you planned %s test but ran %s extra.', $this->stats[$file]['plan'], $this->stats[$file]['nb_tests'] - $this->stats[$file]['plan']), 'COMMENT')));
$this->stats[$file]['status'] = 'dubious';
$this->stats[$file]['status_code'] = 255;
for ($i = 1; $i <= -$delta; $i++)
{
$this->stats[$file]['failed'][] = $this->stats[$file]['plan'] + $i;
}
}
else
{
$this->stats[$file]['status_code'] = 0;
$this->stats[$file]['status'] = $this->stats[$file]['failed'] ? 'not ok' : 'ok';
}
}
$this->output->echoln(sprintf('%s%s%s', substr($relative_file, -min(67, strlen($relative_file))), str_repeat('.', 70 - min(67, strlen($relative_file))), $this->stats[$file]['status']));
if (($nb = count($this->stats[$file]['failed'])) || $return > 0)
{
if ($nb)
{
$this->output->echoln(sprintf(" Failed tests: %s", implode(', ', $this->stats[$file]['failed'])));
}
$this->stats['_failed_files'][] = $file;
$this->stats['_failed_tests'] += $nb;
}
if ('dubious' == $this->stats[$file]['status'])
{
$this->output->echoln(sprintf(' Test returned status %s', $this->stats[$file]['status_code']));
}
}
if (count($this->stats['_failed_files']))
{
$format = "%-30s %4s %5s %5s %s";
$this->output->echoln(sprintf($format, 'Failed Test', 'Stat', 'Total', 'Fail', 'List of Failed'));
$this->output->echoln("------------------------------------------------------------------");
foreach ($this->stats as $file => $file_stat)
{
if (!in_array($file, $this->stats['_failed_files'])) continue;
$relative_file = $this->get_relative_file($file);
$this->output->echoln(sprintf($format, substr($relative_file, -min(30, strlen($relative_file))), $file_stat['status_code'], count($file_stat['failed']) + count($file_stat['passed']), count($file_stat['failed']), implode(' ', $file_stat['failed'])));
}
$this->output->red_bar(sprintf('Failed %d/%d test scripts, %.2f%% okay. %d/%d subtests failed, %.2f%% okay.',
$nb_failed_files = count($this->stats['_failed_files']),
$nb_files = count($this->files),
($nb_files - $nb_failed_files) * 100 / $nb_files,
$nb_failed_tests = $this->stats['_failed_tests'],
$nb_tests = $this->stats['_nb_tests'],
$nb_tests > 0 ? ($nb_tests - $nb_failed_tests) * 100 / $nb_tests : 0
));
}
else
{
$this->output->green_bar(' All tests successful.');
$this->output->green_bar(sprintf(' Files=%d, Tests=%d', count($this->files), $this->stats['_nb_tests']));
}
return $this->stats['_failed_tests'] ? false : true;
}
private function process_test_output($lines)
{
foreach (explode("\n", $lines) as $text)
{
if (false !== strpos($text, 'not ok '))
{
++$this->current_test;
$test_number = (int) substr($text, 7);
$this->stats[$this->current_file]['failed'][] = $test_number;
++$this->stats[$this->current_file]['nb_tests'];
++$this->stats['_nb_tests'];
}
else if (false !== strpos($text, 'ok '))
{
++$this->stats[$this->current_file]['nb_tests'];
++$this->stats['_nb_tests'];
}
else if (preg_match('/^1\.\.(\d+)/', $text, $match))
{
$this->stats[$this->current_file]['plan'] = $match[1];
}
}
return;
}
}
class lime_coverage extends lime_registration
{
public $files = array();
public $extension = '.php';
public $base_dir = '';
public $harness = null;
public $verbose = false;
function __construct($harness)
{
$this->harness = $harness;
}
function run()
{
if (!function_exists('xdebug_start_code_coverage'))
{
throw new Exception('You must install and enable xdebug before using lime coverage.');
}
if (!ini_get('xdebug.extended_info'))
{
throw new Exception('You must set xdebug.extended_info to 1 in your php.ini to use lime coverage.');
}
if (!count($this->harness->files))
{
throw new Exception('You must register some test files before running coverage!');
}
if (!count($this->files))
{
throw new Exception('You must register some files to cover!');
}
$coverage = array();
$tmp_file = lime_test::get_temp_directory().DIRECTORY_SEPARATOR.'test.php';
foreach ($this->harness->files as $file)
{
$tmp = <<<EOF
<?php
xdebug_start_code_coverage();
ob_start();
include('$file');
ob_end_clean();
echo '<PHP_SER>'.serialize(xdebug_get_code_coverage()).'</PHP_SER>';
EOF;
file_put_contents($tmp_file, $tmp);
ob_start();
passthru(sprintf('%s -d html_errors=off -d open_basedir= -q "%s" 2>&1', $this->harness->php_cli, $tmp_file), $return);
$retval = ob_get_clean();
if (0 == $return)
{
if (false === $cov = unserialize(substr($retval, strpos($retval, '<PHP_SER>') + 9, strpos($retval, '</PHP_SER>') - 9)))
{
throw new Exception(sprintf('Unable to unserialize coverage for file "%s"', $file));
}
foreach ($cov as $file => $lines)
{
if (!isset($coverage[$file]))
{
$coverage[$file] = array();
}
foreach ($lines as $line => $count)
{
if (!isset($coverage[$file][$line]))
{
$coverage[$file][$line] = 0;
}
$coverage[$file][$line] = $coverage[$file][$line] + $count;
}
}
}
}
unlink($tmp_file);
ksort($coverage);
$total_php_lines = 0;
$total_covered_lines = 0;
foreach ($this->files as $file)
{
$cov = isset($coverage[$file]) ? $coverage[$file] : array();
list($cov, $php_lines) = $this->compute(file_get_contents($file), $cov);
$output = $this->harness->output;
$percent = count($php_lines) ? count($cov) * 100 / count($php_lines) : 100;
$total_php_lines += count($php_lines);
$total_covered_lines += count($cov);
$relative_file = $this->get_relative_file($file);
$output->echoln(sprintf("%-70s %3.0f%%", substr($relative_file, -min(70, strlen($relative_file))), $percent), $percent == 100 ? 'INFO' : ($percent > 90 ? 'PARAMETER' : ($percent < 20 ? 'ERROR' : '')));
if ($this->verbose && $percent != 100)
{
$output->comment(sprintf("missing: %s", $this->format_range(array_keys(array_diff_key($php_lines, $cov)))));
}
}
$output->echoln(sprintf("TOTAL COVERAGE: %3.0f%%", $total_covered_lines * 100 / $total_php_lines));
}
static function get_php_lines($content)
{
if (is_file($content))
{
$content = file_get_contents($content);
}
$tokens = token_get_all($content);
$php_lines = array();
$current_line = 1;
$in_class = false;
$in_function = false;
$in_function_declaration = false;
$end_of_current_expr = true;
$open_braces = 0;
foreach ($tokens as $token)
{
if (is_string($token))
{
switch ($token)
{
case '=':
if (false === $in_class || (false !== $in_function && !$in_function_declaration))
{
$php_lines[$current_line] = true;
}
break;
case '{':
++$open_braces;
$in_function_declaration = false;
break;
case ';':
$in_function_declaration = false;
$end_of_current_expr = true;
break;
case '}':
$end_of_current_expr = true;
--$open_braces;
if ($open_braces == $in_class)
{
$in_class = false;
}
if ($open_braces == $in_function)
{
$in_function = false;
}
break;
}
continue;
}
list($id, $text) = $token;
switch ($id)
{
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
++$open_braces;
break;
case T_WHITESPACE:
case T_OPEN_TAG:
case T_CLOSE_TAG:
$end_of_current_expr = true;
$current_line += count(explode("\n", $text)) - 1;
break;
case T_COMMENT:
case T_DOC_COMMENT:
$current_line += count(explode("\n", $text)) - 1;
break;
case T_CLASS:
$in_class = $open_braces;
break;
case T_FUNCTION:
$in_function = $open_braces;
$in_function_declaration = true;
break;
case T_AND_EQUAL:
case T_CASE:
case T_CATCH:
case T_CLONE:
case T_CONCAT_EQUAL:
case T_CONTINUE:
case T_DEC:
case T_DECLARE:
case T_DEFAULT:
case T_DIV_EQUAL:
case T_DO:
case T_ECHO:
case T_ELSEIF:
case T_EMPTY:
case T_ENDDECLARE:
case T_ENDFOR:
case T_ENDFOREACH:
case T_ENDIF:
case T_ENDSWITCH:
case T_ENDWHILE:
case T_EVAL:
case T_EXIT:
case T_FOR:
case T_FOREACH:
case T_GLOBAL:
case T_IF:
case T_INC:
case T_INCLUDE:
case T_INCLUDE_ONCE:
case T_INSTANCEOF:
case T_ISSET:
case T_IS_EQUAL:
case T_IS_GREATER_OR_EQUAL:
case T_IS_IDENTICAL:
case T_IS_NOT_EQUAL:
case T_IS_NOT_IDENTICAL:
case T_IS_SMALLER_OR_EQUAL:
case T_LIST:
case T_LOGICAL_AND:
case T_LOGICAL_OR:
case T_LOGICAL_XOR:
case T_MINUS_EQUAL:
case T_MOD_EQUAL:
case T_MUL_EQUAL:
case T_NEW:
case T_OBJECT_OPERATOR:
case T_OR_EQUAL:
case T_PLUS_EQUAL:
case T_PRINT:
case T_REQUIRE:
case T_REQUIRE_ONCE:
case T_RETURN:
case T_SL:
case T_SL_EQUAL:
case T_SR:
case T_SR_EQUAL:
case T_THROW:
case T_TRY:
case T_UNSET:
case T_UNSET_CAST:
case T_USE:
case T_WHILE:
case T_XOR_EQUAL:
$php_lines[$current_line] = true;
$end_of_current_expr = false;
break;
default:
if (false === $end_of_current_expr)
{
$php_lines[$current_line] = true;
}
//print "$current_line: ".token_name($id)."\n";
}
}
return $php_lines;
}
function compute($content, $cov)
{
$php_lines = self::get_php_lines($content);
// we remove from $cov non php lines
foreach (array_diff_key($cov, $php_lines) as $line => $tmp)
{
unset($cov[$line]);
}
return array($cov, $php_lines);
}
function format_range($lines)
{
sort($lines);
$formatted = '';
$first = -1;
$last = -1;
foreach ($lines as $line)
{
if ($last + 1 != $line)
{
if ($first != -1)
{
$formatted .= $first == $last ? "$first " : "[$first - $last] ";
}
$first = $line;
$last = $line;
}
else
{
$last = $line;
}
}
if ($first != -1)
{
$formatted .= $first == $last ? "$first " : "[$first - $last] ";
}
return $formatted;
}
}
class lime_registration
{
public $files = array();
public $extension = '.php';
public $base_dir = '';
function register($files_or_directories)
{
foreach ((array) $files_or_directories as $f_or_d)
{
if (is_file($f_or_d))
{
$this->files[] = realpath($f_or_d);
}
elseif (is_dir($f_or_d))
{
$this->register_dir($f_or_d);
}
else
{
throw new Exception(sprintf('The file or directory "%s" does not exist.', $f_or_d));
}
}
}
function register_glob($glob)
{
if ($dirs = glob($glob))
{
foreach ($dirs as $file)
{
$this->files[] = realpath($file);
}
}
}
function register_dir($directory)
{
if (!is_dir($directory))
{
throw new Exception(sprintf('The directory "%s" does not exist.', $directory));
}
$files = array();
$current_dir = opendir($directory);
while ($entry = readdir($current_dir))
{
if ($entry == '.' || $entry == '..') continue;
if (is_dir($entry))
{
$this->register_dir($entry);
}
elseif (preg_match('#'.$this->extension.'$#', $entry))
{
$files[] = realpath($directory.DIRECTORY_SEPARATOR.$entry);
}
}
$this->files = array_merge($this->files, $files);
}
protected function get_relative_file($file)
{
return str_replace(DIRECTORY_SEPARATOR, '/', str_replace(array(realpath($this->base_dir).DIRECTORY_SEPARATOR, $this->extension), '', $file));
}
}

458
lib/symfony/vendor/pake/pakeApp.class.php vendored Executable file
View File

@ -0,0 +1,458 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeApp.class.php 4623 2007-07-16 12:34:38Z fabien $
*/
/**
*
* main pake class.
*
* This class is a singleton.
*
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeApp.class.php 4623 2007-07-16 12:34:38Z fabien $
*/
class pakeApp
{
const VERSION = '1.1.DEV';
private static $MAX_LINE_SIZE = 65;
private static $PROPERTIES = array();
private static $PAKEFILES = array('pakefile', 'Pakefile', 'pakefile.php', 'Pakefile.php');
private static $PLUGINDIRS = array();
private static $OPTIONS = array(
array('--dry-run', '-n', pakeGetopt::NO_ARGUMENT, "Do a dry run without executing actions."),
array('--help', '-H', pakeGetopt::NO_ARGUMENT, "Display this help message."),
array('--libdir', '-I', pakeGetopt::REQUIRED_ARGUMENT, "Include LIBDIR in the search path for required modules."),
array('--nosearch', '-N', pakeGetopt::NO_ARGUMENT, "Do not search parent directories for the pakefile."),
array('--prereqs', '-P', pakeGetopt::NO_ARGUMENT, "Display the tasks and dependencies, then exit."),
array('--quiet', '-q', pakeGetopt::NO_ARGUMENT, "Do not log messages to standard output."),
array('--pakefile', '-f', pakeGetopt::REQUIRED_ARGUMENT, "Use FILE as the pakefile."),
array('--require', '-r', pakeGetopt::REQUIRED_ARGUMENT, "Require MODULE before executing pakefile."),
array('--tasks', '-T', pakeGetopt::NO_ARGUMENT, "Display the tasks and dependencies, then exit."),
array('--trace', '-t', pakeGetopt::NO_ARGUMENT, "Turn on invoke/execute tracing, enable full backtrace."),
array('--usage', '-h', pakeGetopt::NO_ARGUMENT, "Display usage."),
array('--verbose', '-v', pakeGetopt::NO_ARGUMENT, "Log message to standard output (default)."),
array('--version', '-V', pakeGetopt::NO_ARGUMENT, "Display the program version."),
);
private $opt = null;
private $nosearch = false;
private $trace = false;
private $verbose = true;
private $dryrun = false;
private $nowrite = false;
private $show_tasks = false;
private $show_prereqs = false;
private $pakefile = '';
private static $instance = null;
private function __construct()
{
self::$PLUGINDIRS[] = dirname(__FILE__).'/tasks';
}
public static function get_plugin_dirs()
{
return self::$PLUGINDIRS;
}
public function get_properties()
{
return self::$PROPERTIES;
}
public function set_properties($properties)
{
self::$PROPERTIES = $properties;
}
public static function get_instance()
{
if (!self::$instance) self::$instance = new pakeApp();
return self::$instance;
}
public function get_verbose()
{
return $this->verbose;
}
public function get_trace()
{
return $this->trace;
}
public function get_dryrun()
{
return $this->dryrun;
}
public function run($pakefile = null, $options = null, $load_pakefile = true)
{
if ($pakefile)
{
pakeApp::$PAKEFILES = array($pakefile);
}
$this->handle_options($options);
if ($load_pakefile)
{
$this->load_pakefile();
}
if ($this->show_tasks)
{
$this->display_tasks_and_comments();
}
else if ($this->show_prereqs)
{
$this->display_prerequisites();
}
else
{
$args = $this->opt->get_arguments();
$task = array_shift($args);
$options = array();
for ($i = 0, $max = count($args); $i < $max; $i++)
{
if (0 === strpos($args[$i], '--'))
{
if (false !== $pos = strpos($args[$i], '='))
{
$key = substr($args[$i], 2, $pos - 2);
$value = substr($args[$i], $pos + 1);
}
else
{
$key = substr($args[$i], 2);
$value = true;
}
if ('[]' == substr($key, -2))
{
if (!isset($options[$key]))
{
$options[$key] = array();
}
$options[$key][] = $value;
}
else
{
$options[$key] = $value;
}
unset($args[$i]);
}
}
$args = array_values($args);
$abbrev_options = $this->abbrev(array_keys(pakeTask::get_tasks()));
$task = pakeTask::get_full_task_name($task);
if (!$task)
{
$task = 'default';
}
if (!array_key_exists($task, $abbrev_options))
{
throw new pakeException(sprintf('Task "%s" is not defined.', $task));
}
else if (count($abbrev_options[$task]) > 1)
{
throw new pakeException(sprintf('Task "%s" is ambiguous (%s).', $task, implode(', ', $abbrev_options[$task])));
}
else
{
return pakeTask::get($abbrev_options[$task][0])->invoke($args, $options);
}
}
}
// Read and handle the command line options.
public function handle_options($options = null)
{
$this->opt = new pakeGetopt(pakeApp::$OPTIONS);
$this->opt->parse($options);
foreach ($this->opt->get_options() as $opt => $value)
{
$this->do_option($opt, $value);
}
}
// True if one of the files in RAKEFILES is in the current directory.
// If a match is found, it is copied into @pakefile.
public function have_pakefile()
{
foreach (pakeApp::$PAKEFILES as $file)
{
if (file_exists($file))
{
$this->pakefile = $file;
return true;
}
}
return false;
}
public function load_pakefile()
{
$here = getcwd();
while (!$this->have_pakefile())
{
chdir('..');
if (getcwd() == $here || $this->nosearch)
{
throw new pakeException(sprintf('No pakefile found (looking for: %s)', join(', ', pakeApp::$PAKEFILES))."\n");
}
$here = getcwd();
}
require_once($this->pakefile);
}
// Do the option defined by +opt+ and +value+.
public function do_option($opt, $value)
{
switch ($opt)
{
case 'dry-run':
$this->verbose = true;
$this->nowrite = true;
$this->dryrun = true;
$this->trace = true;
break;
case 'help':
$this->help();
exit();
case 'libdir':
set_include_path($value.PATH_SEPARATOR.get_include_path());
break;
case 'nosearch':
$this->nosearch = true;
break;
case 'prereqs':
$this->show_prereqs = true;
break;
case 'quiet':
$this->verbose = false;
break;
case 'pakefile':
pakeApp::$PAKEFILES = array($value);
break;
case 'require':
require $value;
break;
case 'tasks':
$this->show_tasks = true;
break;
case 'trace':
$this->trace = true;
$this->verbose = true;
break;
case 'usage':
$this->usage();
exit();
case 'verbose':
$this->verbose = true;
break;
case 'version':
echo sprintf('pake version %s', pakeColor::colorize(pakeApp::VERSION, 'INFO'))."\n";
exit();
default:
throw new pakeException(sprintf("Unknown option: %s", $opt));
}
}
// Display the program usage line.
public function usage()
{
echo "pake [-f pakefile] {options} targets...\n".pakeColor::colorize("Try pake -H for more information", 'INFO')."\n";
}
// Display the rake command line help.
public function help()
{
$this->usage();
echo "\n";
echo "available options:";
echo "\n";
foreach (pakeApp::$OPTIONS as $option)
{
list($long, $short, $mode, $comment) = $option;
if ($mode == pakeGetopt::REQUIRED_ARGUMENT)
{
if (preg_match('/\b([A-Z]{2,})\b/', $comment, $match))
$long .= '='.$match[1];
}
printf(" %-20s (%s)\n", pakeColor::colorize($long, 'INFO'), pakeColor::colorize($short, 'INFO'));
printf(" %s\n", $comment);
}
}
// Display the tasks and dependencies.
public function display_tasks_and_comments()
{
$width = 0;
$tasks = pakeTask::get_tasks();
foreach ($tasks as $name => $task)
{
$w = strlen(pakeTask::get_mini_task_name($name));
if ($w > $width) $width = $w;
}
$width += strlen(pakeColor::colorize(' ', 'INFO'));
echo "available pake tasks:\n";
// display tasks
$has_alias = false;
ksort($tasks);
foreach ($tasks as $name => $task)
{
if ($task->get_alias())
{
$has_alias = true;
}
if (!$task->get_alias() && $task->get_comment())
{
$mini_name = pakeTask::get_mini_task_name($name);
printf(' %-'.$width.'s > %s'."\n", pakeColor::colorize($mini_name, 'INFO'), $task->get_comment().($mini_name != $name ? ' ['.$name.']' : ''));
}
}
if ($has_alias)
{
print("\ntask aliases:\n");
// display aliases
foreach ($tasks as $name => $task)
{
if ($task->get_alias())
{
$mini_name = pakeTask::get_mini_task_name($name);
printf(' %-'.$width.'s = pake %s'."\n", pakeColor::colorize(pakeTask::get_mini_task_name($name), 'INFO'), $task->get_alias().($mini_name != $name ? ' ['.$name.']' : ''));
}
}
}
}
// Display the tasks and prerequisites
public function display_prerequisites()
{
foreach (pakeTask::get_tasks() as $name => $task)
{
echo "pake ".pakeTask::get_mini_task_name($name)."\n";
foreach ($task->get_prerequisites() as $prerequisite)
{
echo " $prerequisite\n";
}
}
}
public static function get_files_from_argument($arg, $target_dir = '', $relative = false)
{
$files = array();
if (is_array($arg))
{
$files = $arg;
}
else if (is_string($arg))
{
$files[] = $arg;
}
else if ($arg instanceof pakeFinder)
{
$files = $arg->in($target_dir);
}
else
{
throw new pakeException('Wrong argument type (must be a list, a string or a pakeFinder object).');
}
if ($relative && $target_dir)
{
$files = preg_replace('/^'.preg_quote(realpath($target_dir), '/').'/', '', $files);
// remove leading /
$files = array_map(create_function('$f', 'return 0 === strpos($f, DIRECTORY_SEPARATOR) ? substr($f, 1) : $f;'), $files);
}
return $files;
}
public static function excerpt($text, $size = null)
{
if (!$size)
{
$size = self::$MAX_LINE_SIZE;
}
if (strlen($text) < $size)
{
return $text;
}
$subsize = floor(($size - 3) / 2);
return substr($text, 0, $subsize).pakeColor::colorize('...', 'INFO').substr($text, -$subsize);
}
/* see perl Text::Abbrev module */
private function abbrev($options)
{
$abbrevs = array();
$table = array();
foreach ($options as $option)
{
$option = pakeTask::get_mini_task_name($option);
for ($len = (strlen($option)) - 1; $len > 0; --$len)
{
$abbrev = substr($option, 0, $len);
if (!array_key_exists($abbrev, $table))
$table[$abbrev] = 1;
else
++$table[$abbrev];
$seen = $table[$abbrev];
if ($seen == 1)
{
// we're the first word so far to have this abbreviation.
$abbrevs[$abbrev] = array($option);
}
else if ($seen == 2)
{
// we're the second word to have this abbreviation, so we can't use it.
//unset($abbrevs[$abbrev]);
$abbrevs[$abbrev][] = $option;
}
else
{
// we're the third word to have this abbreviation, so skip to the next word.
continue;
}
}
}
// Non-abbreviations always get entered, even if they aren't unique
foreach ($options as $option)
{
$abbrevs[$option] = array($option);
}
return $abbrevs;
}
}

72
lib/symfony/vendor/pake/pakeColor.class.php vendored Executable file
View File

@ -0,0 +1,72 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeColor.class.php 2990 2006-12-09 11:10:59Z fabien $
*/
/**
*
* main pake class.
*
* This class is a singleton.
*
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeColor.class.php 2990 2006-12-09 11:10:59Z fabien $
*/
class pakeColor
{
static public $styles = array();
static function style($name, $options = array())
{
self::$styles[$name] = $options;
}
static function colorize($text = '', $parameters = array(), $stream = STDOUT)
{
// disable colors if not supported (windows or non tty console)
if (DIRECTORY_SEPARATOR == '\\' || !function_exists('posix_isatty') || !@posix_isatty($stream))
{
return $text;
}
static $options = array('bold' => 1, 'underscore' => 4, 'blink' => 5, 'reverse' => 7, 'conceal' => 8);
static $foreground = array('black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'white' => 37);
static $background = array('black' => 40, 'red' => 41, 'green' => 42, 'yellow' => 43, 'blue' => 44, 'magenta' => 45, 'cyan' => 46, 'white' => 47);
if (!is_array($parameters) && isset(self::$styles[$parameters]))
{
$parameters = self::$styles[$parameters];
}
$codes = array();
if (isset($parameters['fg']))
{
$codes[] = $foreground[$parameters['fg']];
}
if (isset($parameters['bg']))
{
$codes[] = $background[$parameters['bg']];
}
foreach ($options as $option => $value)
{
if (isset($parameters[$option]) && $parameters[$option])
{
$codes[] = $value;
}
}
return "\033[".implode(';', $codes).'m'.$text."\033[0m";
}
}
pakeColor::style('ERROR', array('bg' => 'red', 'fg' => 'white', 'bold' => true));
pakeColor::style('INFO', array('fg' => 'green', 'bold' => true));
pakeColor::style('COMMENT', array('fg' => 'yellow'));

View File

@ -0,0 +1,91 @@
<?php
/*
* This file is part of the pake package.
* (c) 2004, 2005 Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* pakeException is the base class for all pake related exceptions and
* provides an additional method for printing up a detailed view of an
* exception.
*
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @version SVN: $Id: pakeException.class.php 2795 2006-11-23 19:51:21Z fabien $
*/
class pakeException extends Exception
{
public static function strlen($string)
{
return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string);
}
function render($e)
{
$title = ' ['.get_class($e).'] ';
$len = self::strlen($title);
$lines = array();
foreach (explode("\n", $e->getMessage()) as $line)
{
$lines[] = ' '.$line.' ';
$len = max(self::strlen($line) + 4, $len);
}
$messages = array(
str_repeat(' ', $len),
$title.str_repeat(' ', $len - self::strlen($title)),
);
foreach ($lines as $line)
{
$messages[] = $line.str_repeat(' ', $len - self::strlen($line));
}
$messages[] = str_repeat(' ', $len);
fwrite(STDERR, "\n");
foreach ($messages as $message)
{
fwrite(STDERR, pakeColor::colorize($message, 'ERROR', STDERR)."\n");
}
fwrite(STDERR, "\n");
$pake = pakeApp::get_instance();
if ($pake->get_trace())
{
fwrite(STDERR, "exception trace:\n");
$trace = $this->trace($e);
for ($i = 0, $count = count($trace); $i < $count; $i++)
{
$class = (isset($trace[$i]['class']) ? $trace[$i]['class'] : '');
$type = (isset($trace[$i]['type']) ? $trace[$i]['type'] : '');
$function = $trace[$i]['function'];
$file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
fwrite(STDERR, sprintf(" %s%s%s at %s:%s\n", $class, $type, $function, pakeColor::colorize($file, 'INFO', STDERR), pakeColor::colorize($line, 'INFO', STDERR)));
}
}
fwrite(STDERR, "\n");
}
function trace($exception)
{
// exception related properties
$trace = $exception->getTrace();
array_unshift($trace, array(
'function' => '',
'file' => ($exception->getFile() != null) ? $exception->getFile() : 'n/a',
'line' => ($exception->getLine() != null) ? $exception->getLine() : 'n/a',
'args' => array(),
));
return $trace;
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeFileTask.class.php 1791 2006-08-23 21:17:06Z fabien $
*/
/**
*
* .
*
* .
*
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeFileTask.class.php 1791 2006-08-23 21:17:06Z fabien $
*/
class pakeFileTask extends pakeTask
{
public function is_needed()
{
if (!file_exists($this->get_name())) return true;
$latest_prereq = 0;
foreach ($this->prerequisites as $prerequisite)
{
$t = pakeTask::get($prerequisite)->timestamp();
if ($t > $latest_prereq)
{
$latest_prereq = $t;
}
}
if ($latest_prereq == 0)
{
return false;
}
return ($this->timestamp() < $latest_prereq);
}
public function timestamp()
{
if (!file_exists($this->get_name()))
{
throw new pakeException(sprintf('File "%s" does not exist!', $this->get_name()));
}
$stats = stat($this->get_name());
return $stats['mtime'];
}
public static function define_task($name, $deps = null)
{
$task = pakeTask::lookup($name, 'pakeFileTask');
$task->add_comment();
$task->enhance($deps);
}
}

538
lib/symfony/vendor/pake/pakeFinder.class.php vendored Executable file
View File

@ -0,0 +1,538 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeFinder.class.php 3268 2007-01-13 20:19:33Z fabien $
*/
require_once dirname(__FILE__).'/pakeGlobToRegex.class.php';
require_once dirname(__FILE__).'/pakeNumberCompare.class.php';
if (class_exists('pakeFinder'))
{
return;
}
/**
*
* Allow to build rules to find files and directories.
*
* All rules may be invoked several times, except for ->in() method.
* Some rules are cumulative (->name() for example) whereas others are destructive
* (most recent value is used, ->maxdepth() method for example).
*
* All methods return the current pakeFinder object to allow easy chaining:
*
* $files = pakeFinder::type('file')->name('*.php')->in(.);
*
* Interface loosely based on perl File::Find::Rule module.
*
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeFinder.class.php 3268 2007-01-13 20:19:33Z fabien $
*/
class pakeFinder
{
private $type = 'file';
private $names = array();
private $prunes = array();
private $discards = array();
private $execs = array();
private $mindepth = 0;
private $sizes = array();
private $maxdepth = 1000000;
private $relative = false;
private $follow_link = false;
private $search_dir = '';
/**
* Sets maximum directory depth.
*
* Finder will descend at most $level levels of directories below the starting point.
*
* @param integer level
* @return object current pakeFinder object
*/
public function maxdepth($level)
{
$this->maxdepth = $level;
return $this;
}
/**
* Sets minimum directory depth.
*
* Finder will start applying tests at level $level.
*
* @param integer level
* @return object current pakeFinder object
*/
public function mindepth($level)
{
$this->mindepth = $level;
return $this;
}
public function get_type()
{
return $this->type;
}
/**
* Sets the type of elements to returns.
*
* @param string directory or file or any (for both file and directory)
* @return object new pakeFinder object
*/
public static function type($name)
{
$finder = new pakeFinder();
if (strtolower(substr($name, 0, 3)) == 'dir')
{
$finder->type = 'directory';
}
else if (strtolower($name) == 'any')
{
$finder->type = 'any';
}
else
{
$finder->type = 'file';
}
return $finder;
}
/*
* glob, patterns (must be //) or strings
*/
private function to_regex($str)
{
if ($str[0] == '/' && $str[strlen($str) - 1] == '/')
{
return $str;
}
else
{
return pakeGlobToRegex::glob_to_regex($str);
}
}
private function args_to_array($arg_list, $not = false)
{
$list = array();
for ($i = 0; $i < count($arg_list); $i++)
{
if (is_array($arg_list[$i]))
{
foreach ($arg_list[$i] as $arg)
{
$list[] = array($not, $this->to_regex($arg));
}
}
else
{
$list[] = array($not, $this->to_regex($arg_list[$i]));
}
}
return $list;
}
/**
* Adds rules that files must match.
*
* You can use patterns (delimited with / sign), globs or simple strings.
*
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('test.php')
*
* @param list a list of patterns, globs or strings
* @return object current pakeFinder object
*/
public function name()
{
$args = func_get_args();
$this->names = array_merge($this->names, $this->args_to_array($args));
return $this;
}
/**
* Adds rules that files must not match.
*
* @see ->name()
* @param list a list of patterns, globs or strings
* @return object current pakeFinder object
*/
public function not_name()
{
$args = func_get_args();
$this->names = array_merge($this->names, $this->args_to_array($args, true));
return $this;
}
/**
* Adds tests for file sizes.
*
* $finder->size('> 10K');
* $finder->size('<= 1Ki');
* $finder->size(4);
*
* @param list a list of comparison strings
* @return object current pakeFinder object
*/
public function size()
{
$args = func_get_args();
for ($i = 0; $i < count($args); $i++)
{
$this->sizes[] = new pakeNumberCompare($args[$i]);
}
return $this;
}
/**
* Traverses no further.
*
* @param list a list of patterns, globs to match
* @return object current pakeFinder object
*/
public function prune()
{
$args = func_get_args();
$this->prunes = array_merge($this->prunes, $this->args_to_array($args));
return $this;
}
/**
* Discards elements that matches.
*
* @param list a list of patterns, globs to match
* @return object current pakeFinder object
*/
public function discard()
{
$args = func_get_args();
$this->discards = array_merge($this->discards, $this->args_to_array($args));
return $this;
}
/**
* Ignores version control directories.
*
* Currently supports subversion, CVS, DARCS, Gnu Arch, Monotone, Bazaar-NG
*
* @return object current pakeFinder object
*/
public function ignore_version_control()
{
$ignores = array('.svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr');
return $this->discard($ignores)->prune($ignores);
}
/**
* Executes function or method for each element.
*
* Element match if functino or method returns true.
*
* $finder->exec('myfunction');
* $finder->exec(array($object, 'mymethod'));
*
* @param mixed function or method to call
* @return object current pakeFinder object
*/
public function exec()
{
$args = func_get_args();
for ($i = 0; $i < count($args); $i++)
{
if (is_array($args[$i]) && !method_exists($args[$i][0], $args[$i][1]))
{
throw new pakeException(sprintf("Method %s does not exist for object %s.", $args[$i][1], $args[$i][0]));
}
else if (!is_array($args[$i]) && !function_exists($args[$i]))
{
throw new pakeException(sprintf("Function %s does not exist.", $args[$i]));
}
$this->execs[] = $args[$i];
}
return $this;
}
/**
* Returns relative paths for all files and directories.
*
* @return object current pakeFinder object
*/
public function relative()
{
$this->relative = true;
return $this;
}
/**
* Symlink following.
*
* @return object current sfFinder object
*/
public function follow_link()
{
$this->follow_link = true;
return $this;
}
/**
* Searches files and directories which match defined rules.
*
* @return array list of files and directories
*/
public function in()
{
$files = array();
$here_dir = getcwd();
$numargs = func_num_args();
$arg_list = func_get_args();
// first argument is an array?
if ($numargs == 1 && is_array($arg_list[0]))
{
$arg_list = $arg_list[0];
$numargs = count($arg_list);
}
$dirs = array();
for ($i = 0; $i < $numargs; $i++)
{
if ($argDirs = glob($arg_list[$i]))
{
$dirs = array_merge($dirs, $argDirs);
}
}
foreach ($dirs as $dir)
{
$real_dir = realpath($dir);
// absolute path?
if (!self::isPathAbsolute($real_dir))
{
$dir = $here_dir.DIRECTORY_SEPARATOR.$real_dir;
}
else
{
$dir = $real_dir;
}
if (!is_dir($real_dir))
{
continue;
}
$this->search_dir = $dir;
if ($this->relative)
{
$files = array_merge($files, str_replace($dir.DIRECTORY_SEPARATOR, '', $this->search_in($dir)));
}
else
{
$files = array_merge($files, $this->search_in($dir));
}
}
return array_unique($files);
}
private function search_in($dir, $depth = 0)
{
if ($depth > $this->maxdepth)
{
return array();
}
if (is_link($dir) && !$this->follow_link)
{
return array();
}
$files = array();
if (is_dir($dir))
{
$current_dir = opendir($dir);
while (false !== $entryname = readdir($current_dir))
{
if ($entryname == '.' || $entryname == '..') continue;
$current_entry = $dir.DIRECTORY_SEPARATOR.$entryname;
if (is_link($current_entry) && !$this->follow_link)
{
continue;
}
if (is_dir($current_entry))
{
if (($this->type == 'directory' || $this->type == 'any') && ($depth >= $this->mindepth) && !$this->is_discarded($dir, $entryname) && $this->match_names($dir, $entryname) && $this->exec_ok($dir, $entryname))
{
$files[] = realpath($current_entry);
}
if (!$this->is_pruned($dir, $entryname))
{
$files = array_merge($files, $this->search_in($current_entry, $depth + 1));
}
}
else
{
if (($this->type != 'directory' || $this->type == 'any') && ($depth >= $this->mindepth) && !$this->is_discarded($dir, $entryname) && $this->match_names($dir, $entryname) && $this->size_ok($dir, $entryname) && $this->exec_ok($dir, $entryname))
{
$files[] = realpath($current_entry);
}
}
}
closedir($current_dir);
}
return $files;
}
private function match_names($dir, $entry)
{
if (!count($this->names)) return true;
// we must match one "not_name" rules to be ko
$one_not_name_rule = false;
foreach ($this->names as $args)
{
list($not, $regex) = $args;
if ($not)
{
$one_not_name_rule = true;
if (preg_match($regex, $entry))
{
return false;
}
}
}
$one_name_rule = false;
// we must match one "name" rules to be ok
foreach ($this->names as $args)
{
list($not, $regex) = $args;
if (!$not)
{
$one_name_rule = true;
if (preg_match($regex, $entry))
{
return true;
}
}
}
if ($one_not_name_rule && $one_name_rule)
{
return false;
}
else if ($one_not_name_rule)
{
return true;
}
else if ($one_name_rule)
{
return false;
}
else
{
return true;
}
}
private function size_ok($dir, $entry)
{
if (!count($this->sizes)) return true;
if (!is_file($dir.DIRECTORY_SEPARATOR.$entry)) return true;
$filesize = filesize($dir.DIRECTORY_SEPARATOR.$entry);
foreach ($this->sizes as $number_compare)
{
if (!$number_compare->test($filesize)) return false;
}
return true;
}
private function is_pruned($dir, $entry)
{
if (!count($this->prunes)) return false;
foreach ($this->prunes as $args)
{
$regex = $args[1];
if (preg_match($regex, $entry)) return true;
}
return false;
}
private function is_discarded($dir, $entry)
{
if (!count($this->discards)) return false;
foreach ($this->discards as $args)
{
$regex = $args[1];
if (preg_match($regex, $entry)) return true;
}
return false;
}
private function exec_ok($dir, $entry)
{
if (!count($this->execs)) return true;
foreach ($this->execs as $exec)
{
if (!call_user_func_array($exec, array($dir, $entry))) return false;
}
return true;
}
public static function isPathAbsolute($path)
{
if ($path{0} == '/' || $path{0} == '\\' ||
(strlen($path) > 3 && ctype_alpha($path{0}) &&
$path{1} == ':' &&
($path{2} == '\\' || $path{2} == '/')
)
)
{
return true;
}
return false;
}
}

429
lib/symfony/vendor/pake/pakeFunction.php vendored Executable file
View File

@ -0,0 +1,429 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeFunction.php 3263 2007-01-13 14:20:52Z fabien $
*/
require_once dirname(__FILE__).'/pakeException.class.php';
require_once dirname(__FILE__).'/pakeYaml.class.php';
require_once dirname(__FILE__).'/pakeGetopt.class.php';
require_once dirname(__FILE__).'/pakeFinder.class.php';
require_once dirname(__FILE__).'/pakeTask.class.php';
require_once dirname(__FILE__).'/pakeFileTask.class.php';
require_once dirname(__FILE__).'/pakeColor.class.php';
require_once dirname(__FILE__).'/pakeApp.class.php';
function pake_import($name, $import_default_tasks = true)
{
$class_name = 'pake'.ucfirst(strtolower($name)).'Task';
if (!class_exists($class_name))
{
// plugin available?
$plugin_path = '';
foreach (pakeApp::get_plugin_dirs() as $dir)
{
if (file_exists($dir.DIRECTORY_SEPARATOR.$class_name.'.class.php'))
{
$plugin_path = $dir.DIRECTORY_SEPARATOR.$class_name.'.class.php';
break;
}
}
if ($plugin_path)
{
require_once $plugin_path;
}
else
{
throw new pakeException(sprintf('Plugin "%s" does not exist.', $name));
}
}
if ($import_default_tasks && is_callable($class_name, 'import_default_tasks'))
{
call_user_func(array($class_name, 'import_default_tasks'));
}
}
function pake_task($name)
{
$args = func_get_args();
array_shift($args);
pakeTask::define_task($name, $args);
return $name;
}
function pake_alias($alias, $name)
{
pakeTask::define_alias($alias, $name);
return $alias;
}
function pake_desc($comment)
{
pakeTask::define_comment($comment);
}
function pake_properties($property_file)
{
$file = $property_file;
if (!pakeFinder::isPathAbsolute($file))
{
$file = getcwd().DIRECTORY_SEPARATOR.$property_file;
}
if (file_exists($file))
{
pakeApp::get_instance()->set_properties(parse_ini_file($file, true));
}
else
{
throw new pakeException('Properties file does not exist.');
}
}
function pake_file($name)
{
$args = func_get_args();
array_shift($args);
pakeFileTask::define_task($name, $args);
return $name;
}
function pake_mkdirs($path, $mode = 0777)
{
if (is_dir($path))
{
return true;
}
pake_echo_action('dir+', $path);
return @mkdir($path, $mode, true);
}
/*
override => boolean
*/
function pake_copy($origin_file, $target_file, $options = array())
{
if (!array_key_exists('override', $options))
{
$options['override'] = false;
}
// we create target_dir if needed
if (!is_dir(dirname($target_file)))
{
pake_mkdirs(dirname($target_file));
}
$most_recent = false;
if (file_exists($target_file))
{
$stat_target = stat($target_file);
$stat_origin = stat($origin_file);
$most_recent = ($stat_origin['mtime'] > $stat_target['mtime']) ? true : false;
}
if ($options['override'] || !file_exists($target_file) || $most_recent)
{
pake_echo_action('file+', $target_file);
copy($origin_file, $target_file);
}
}
function pake_rename($origin, $target, $options = array())
{
// we check that target does not exist
if (is_readable($target))
{
throw new pakeException(sprintf('Cannot rename because the target "%" already exist.', $target));
}
pake_echo_action('rename', $origin.' > '.$target);
rename($origin, $target);
}
function pake_mirror($arg, $origin_dir, $target_dir, $options = array())
{
$files = pakeApp::get_files_from_argument($arg, $origin_dir, true);
foreach ($files as $file)
{
if (is_dir($origin_dir.DIRECTORY_SEPARATOR.$file))
{
pake_mkdirs($target_dir.DIRECTORY_SEPARATOR.$file);
}
else if (is_file($origin_dir.DIRECTORY_SEPARATOR.$file))
{
pake_copy($origin_dir.DIRECTORY_SEPARATOR.$file, $target_dir.DIRECTORY_SEPARATOR.$file, $options);
}
else if (is_link($origin_dir.DIRECTORY_SEPARATOR.$file))
{
pake_symlink($origin_dir.DIRECTORY_SEPARATOR.$file, $target_dir.DIRECTORY_SEPARATOR.$file);
}
else
{
throw new pakeException(sprintf('Unable to determine "%s" type', $file));
}
}
}
function pake_remove($arg, $target_dir)
{
$files = array_reverse(pakeApp::get_files_from_argument($arg, $target_dir));
foreach ($files as $file)
{
if (is_dir($file) && !is_link($file))
{
pake_echo_action('dir-', $file);
rmdir($file);
}
else
{
pake_echo_action(is_link($file) ? 'link-' : 'file-', $file);
unlink($file);
}
}
}
function pake_touch($arg, $target_dir)
{
$files = pakeApp::get_files_from_argument($arg, $target_dir);
foreach ($files as $file)
{
pake_echo_action('file+', $file);
touch($file);
}
}
function pake_replace_tokens($arg, $target_dir, $begin_token, $end_token, $tokens)
{
$files = pakeApp::get_files_from_argument($arg, $target_dir, true);
foreach ($files as $file)
{
$replaced = false;
$content = file_get_contents($target_dir.DIRECTORY_SEPARATOR.$file);
foreach ($tokens as $key => $value)
{
$content = str_replace($begin_token.$key.$end_token, $value, $content, $count);
if ($count) $replaced = true;
}
pake_echo_action('tokens', $target_dir.DIRECTORY_SEPARATOR.$file);
file_put_contents($target_dir.DIRECTORY_SEPARATOR.$file, $content);
}
}
function pake_symlink($origin_dir, $target_dir, $copy_on_windows = false)
{
if (!function_exists('symlink') && $copy_on_windows)
{
$finder = pakeFinder::type('any')->ignore_version_control();
pake_mirror($finder, $origin_dir, $target_dir);
return;
}
$ok = false;
if (is_link($target_dir))
{
if (readlink($target_dir) != $origin_dir)
{
unlink($target_dir);
}
else
{
$ok = true;
}
}
if (!$ok)
{
pake_echo_action('link+', $target_dir);
symlink($origin_dir, $target_dir);
}
}
function pake_chmod($arg, $target_dir, $mode, $umask = 0000)
{
$current_umask = umask();
umask($umask);
$files = pakeApp::get_files_from_argument($arg, $target_dir, true);
foreach ($files as $file)
{
pake_echo_action(sprintf('chmod %o', $mode), $target_dir.DIRECTORY_SEPARATOR.$file);
chmod($target_dir.DIRECTORY_SEPARATOR.$file, $mode);
}
umask($current_umask);
}
function pake_sh($cmd)
{
$verbose = pakeApp::get_instance()->get_verbose();
pake_echo_action('exec ', $cmd);
ob_start();
passthru($cmd.' 2>&1', $return);
$content = ob_get_contents();
ob_end_clean();
if ($return > 0)
{
throw new pakeException(sprintf('Problem executing command %s', $verbose ? "\n".$content : ''));
}
return $content;
}
function pake_strip_php_comments($arg)
{
/* T_ML_COMMENT does not exist in PHP 5.
* The following three lines define it in order to
* preserve backwards compatibility.
*
* The next two lines define the PHP 5-only T_DOC_COMMENT,
* which we will mask as T_ML_COMMENT for PHP 4.
*/
if (!defined('T_ML_COMMENT'))
{
define('T_ML_COMMENT', T_COMMENT);
}
else
{
if (!defined('T_DOC_COMMENT')) define('T_DOC_COMMENT', T_ML_COMMENT);
}
$files = pakeApp::get_files_from_argument($arg);
foreach ($files as $file)
{
if (!is_file($file)) continue;
$source = file_get_contents($file);
$output = '';
$tokens = token_get_all($source);
foreach ($tokens as $token)
{
if (is_string($token))
{
// simple 1-character token
$output .= $token;
}
else
{
// token array
list($id, $text) = $token;
switch ($id)
{
case T_COMMENT:
case T_ML_COMMENT: // we've defined this
case T_DOC_COMMENT: // and this
// no action on comments
break;
default:
// anything else -> output "as is"
$output .= $text;
break;
}
}
}
file_put_contents($file, $output);
}
}
function pake_format_action($section, $text, $size = null)
{
if (pakeApp::get_instance()->get_verbose())
{
$width = 9 + strlen(pakeColor::colorize('', 'INFO'));
return sprintf('>> %-'.$width.'s %s', pakeColor::colorize($section, 'INFO'), pakeApp::excerpt($text, $size))."\n";
}
}
function pake_echo_action($section, $text)
{
echo pake_format_action($section, $text);
}
function pake_excerpt($text)
{
if (pakeApp::get_instance()->get_verbose())
{
echo pakeApp::excerpt($text)."\n";
}
}
function pake_echo($text)
{
if (pakeApp::get_instance()->get_verbose())
{
echo $text."\n";
}
}
function pake_echo_comment($text)
{
if (pakeApp::get_instance()->get_verbose())
{
echo sprintf(pakeColor::colorize(' # %s', 'COMMENT'), $text)."\n";
}
}
// register our default exception handler
function pake_exception_default_handler($exception)
{
$e = new pakeException();
$e->render($exception);
exit(1);
}
set_exception_handler('pake_exception_default_handler');
// fix php behavior if using cgi php
// from http://www.sitepoint.com/article/php-command-line-1/3
if (false !== strpos(PHP_SAPI, 'cgi'))
{
// handle output buffering
@ob_end_flush();
ob_implicit_flush(true);
// PHP ini settings
set_time_limit(0);
ini_set('track_errors', true);
ini_set('html_errors', false);
ini_set('magic_quotes_runtime', false);
// define stream constants
define('STDIN', fopen('php://stdin', 'r'));
define('STDOUT', fopen('php://stdout', 'w'));
define('STDERR', fopen('php://stderr', 'w'));
// change directory
if (isset($_SERVER['PWD']))
{
chdir($_SERVER['PWD']);
}
// close the streams on script termination
register_shutdown_function(create_function('', 'fclose(STDIN); fclose(STDOUT); fclose(STDERR); return true;'));
}

274
lib/symfony/vendor/pake/pakeGetopt.class.php vendored Executable file
View File

@ -0,0 +1,274 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeGetopt.class.php 1791 2006-08-23 21:17:06Z fabien $
*/
if (class_exists('pakeGetopt'))
{
return;
}
/**
*
* Console options parsing class.
*
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeGetopt.class.php 1791 2006-08-23 21:17:06Z fabien $
*/
class pakeGetopt
{
const NO_ARGUMENT = 0;
const REQUIRED_ARGUMENT = 1;
const OPTIONAL_ARGUMENT = 2;
private $short_options = array();
private $long_options = array();
private $args = '';
private $options = array();
private $arguments = array();
public function __construct($options)
{
$this->args = '';
foreach ($options as $option)
{
if (!$option[0])
{
throw new pakeException(sprintf("pakeGetopt: You must define a long option name! for option %s (%s).", $option[1], $option[3]));
}
$this->add_option($option[0], $option[1], $option[2], $option[3]);
}
}
public function add_option($long_opt, $short_opt, $mode = self::NO_ARGUMENT, $comment = '')
{
if ($long_opt{0} == '-' && $long_opt{1} == '-')
{
$long_opt = substr($long_opt, 2);
}
if ($short_opt)
{
if ($short_opt{0} == '-')
{
$short_opt = substr($short_opt, 1);
}
$this->short_options[$short_opt] = array('mode' => $mode, 'comment' => $comment, 'name' => $long_opt);
}
$this->long_options[$long_opt] = array('mode' => $mode, 'comment' => $comment, 'name' => $long_opt);
}
public function parse($args = null)
{
if (is_string($args))
{
// hack to split arguments with spaces : --test="with some spaces"
$args = preg_replace('/(\'|")(.+?)\\1/e', "str_replace(' ', '=PLACEHOLDER=', '\\2')", $args);
$args = preg_split('/\s+/', $args);
$args = str_replace('=PLACEHOLDER=', ' ', $args);
}
else if (!$args)
{
$args = $this->read_php_argv();
// we strip command line program
if (isset($args[0]) && $args[0]{0} != '-')
{
array_shift($args);
}
}
$this->args = $args;
$this->options = array();
$this->arguments = array();
while ($arg = array_shift($this->args))
{
/* '--' stop options parsing. */
if ($arg == '--')
{
$this->arguments = array_merge($this->arguments, $this->args);
break;
}
if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$this->long_options))
{
$this->arguments = array_merge($this->arguments, array($arg), $this->args);
break;
}
elseif (strlen($arg) > 1 && $arg{1} == '-')
{
$this->parse_long_option(substr($arg, 2));
}
else
{
$this->parse_short_option(substr($arg, 1));
}
}
}
public function has_option($option)
{
return (array_key_exists($option, $this->options) ? true : false);
}
public function get_option($option)
{
// is it a long option?
if (array_key_exists($option, $this->long_options) && $this->long_options[$option]['mode'] != self::NO_ARGUMENT)
{
return (array_key_exists($option, $this->options) ? $this->options[$option] : '');
}
else
{
throw new pakeException('pakeGetopt: You cannot get a value for a NO_ARGUMENT option.');
}
}
public function get_options()
{
return $this->options;
}
public function get_arguments()
{
return $this->arguments;
}
private function parse_short_option($arg)
{
for ($i = 0; $i < strlen($arg); $i++)
{
$opt = $arg{$i};
$opt_arg = true;
/* option exists? */
if (!array_key_exists($opt, $this->short_options))
{
throw new pakeException(sprintf("pakeGetopt: unrecognized option -%s.", $opt));
}
/* required or optional argument? */
if ($this->short_options[$opt]['mode'] == self::REQUIRED_ARGUMENT)
{
if ($i + 1 < strlen($arg))
{
$this->options[$this->short_options[$opt]['name']] = substr($arg, $i + 1);
break;
}
else
{
// take next element as argument (if it doesn't start with a -)
if (count($this->args) && $this->args[0]{0} != '-')
{
$this->options[$this->short_options[$opt]['name']] = array_shift($this->args);
break;
}
else
{
throw new pakeException(sprintf("pakeGetopt: option -%s requires an argument", $opt));
}
}
}
else if ($this->short_options[$opt]['mode'] == self::OPTIONAL_ARGUMENT)
{
if (substr($arg, $i + 1) != '')
{
$this->options[$this->short_options[$opt]['name']] = substr($arg, $i + 1);
}
else
{
// take next element as argument (if it doesn't start with a -)
if (count($this->args) && $this->args[0]{0} != '-')
{
$this->options[$this->short_options[$opt]['name']] = array_shift($this->args);
}
else
{
$this->options[$this->short_options[$opt]['name']] = true;
}
}
break;
}
$this->options[$this->short_options[$opt]['name']] = $opt_arg;
}
}
private function parse_long_option($arg)
{
@list($opt, $opt_arg) = explode('=', $arg);
if (!$opt_arg)
{
$opt_arg = true;
}
/* option exists? */
if (!array_key_exists($opt, $this->long_options))
{
throw new pakeException(sprintf("pakeGetopt: unrecognized option --%s.", $opt));
}
/* required or optional argument? */
if ($this->long_options[$opt]['mode'] == self::REQUIRED_ARGUMENT)
{
if ($opt_arg)
{
$this->options[$this->long_options[$opt]['name']] = $opt_arg;
return;
}
else
{
throw new pakeException(sprintf("pakeGetopt: option --%s requires an argument.", $opt));
}
}
else if ($this->long_options[$opt]['mode'] == self::OPTIONAL_ARGUMENT)
{
$this->options[$this->long_options[$opt]['name']] = $opt_arg;
return;
}
else
{
$this->options[$this->long_options[$opt]['name']] = true;
}
}
/**
* Function from PEAR::Console_Getopt.
* Safely read the $argv PHP array across different PHP configurations.
* Will take care on register_globals and register_argc_argv ini directives
*
* @access public
* @return mixed the $argv PHP array
*/
private function read_php_argv()
{
global $argv;
if (!is_array($argv))
{
if (!@is_array($_SERVER['argv']))
{
if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv']))
{
throw new pakeException("pakeGetopt: Could not read cmd args (register_argc_argv=Off?).");
}
return $GLOBALS['HTTP_SERVER_VARS']['argv'];
}
return $_SERVER['argv'];
}
return $argv;
}
}

View File

@ -0,0 +1,139 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com> php port
* @author Richard Clamp <richardc@unixbeard.net> perl version
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeGlobToRegex.class.php 1791 2006-08-23 21:17:06Z fabien $
*/
if (class_exists('pakeGlobToRegex'))
{
return;
}
/**
*
* Match globbing patterns against text.
*
* if match_glob("foo.*", "foo.bar") echo "matched\n";
*
* // prints foo.bar and foo.baz
* $regex = glob_to_regex("foo.*");
* for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t)
* {
* if (/$regex/) echo "matched: $car\n";
* }
*
* pakeGlobToRegex implements glob(3) style matching that can be used to match
* against text, rather than fetching names from a filesystem.
*
* based on perl Text::Glob module.
*
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com> php port
* @author Richard Clamp <richardc@unixbeard.net> perl version
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeGlobToRegex.class.php 1791 2006-08-23 21:17:06Z fabien $
*/
class pakeGlobToRegex
{
private static $strict_leading_dot = true;
private static $strict_wildcard_slash = true;
public static function setStrictLeadingDot($boolean)
{
self::$strict_leading_dot = $boolean;
}
public static function setStrictWildcardSlash($boolean)
{
self::$strict_wildcard_slash = $boolean;
}
/**
* Returns a compiled regex which is the equiavlent of the globbing pattern.
*
* @param string glob pattern
* @return string regex
*/
public static function glob_to_regex($glob)
{
$first_byte = true;
$escaping = false;
$in_curlies = 0;
$regex = '';
for ($i = 0; $i < strlen($glob); $i++)
{
$car = $glob[$i];
if ($first_byte)
{
if (self::$strict_leading_dot && $car != '.')
{
$regex .= '(?=[^\.])';
}
$first_byte = false;
}
if ($car == '/')
{
$first_byte = true;
}
if ($car == '.' || $car == '(' || $car == ')' || $car == '|' || $car == '+' || $car == '^' || $car == '$')
{
$regex .= "\\$car";
}
else if ($car == '*')
{
$regex .= ($escaping ? "\\*" : (self::$strict_wildcard_slash ? "[^/]*" : ".*"));
}
else if ($car == '?')
{
$regex .= ($escaping ? "\\?" : (self::$strict_wildcard_slash ? "[^/]" : "."));
}
else if ($car == '{')
{
$regex .= ($escaping ? "\\{" : "(");
if (!$escaping) ++$in_curlies;
}
else if ($car == '}' && $in_curlies)
{
$regex .= ($escaping ? "}" : ")");
if (!$escaping) --$in_curlies;
}
else if ($car == ',' && $in_curlies)
{
$regex .= ($escaping ? "," : "|");
}
else if ($car == "\\")
{
if ($escaping)
{
$regex .= "\\\\";
$escaping = false;
}
else
{
$escaping = true;
}
continue;
}
else
{
$regex .= $car;
$escaping = false;
}
$escaping = false;
}
return "#^$regex$#";
}
}

View File

@ -0,0 +1,120 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com> php port
* @author Richard Clamp <richardc@unixbeard.net> perl version
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeNumberCompare.class.php 1791 2006-08-23 21:17:06Z fabien $
*/
if (class_exists('pakeNumberCompare'))
{
return;
}
/**
*
* Numeric comparisons.
*
* sfNumberCompare compiles a simple comparison to an anonymous
* subroutine, which you can call with a value to be tested again.
* Now this would be very pointless, if sfNumberCompare didn't understand
* magnitudes.
* The target value may use magnitudes of kilobytes (C<k>, C<ki>),
* megabytes (C<m>, C<mi>), or gigabytes (C<g>, C<gi>). Those suffixed
* with an C<i> use the appropriate 2**n version in accordance with the
* IEC standard: http://physics.nist.gov/cuu/Units/binary.html
*
* based on perl Number::Compare module.
*
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com> php port
* @author Richard Clamp <richardc@unixbeard.net> perl version
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
* @see http://physics.nist.gov/cuu/Units/binary.html
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeNumberCompare.class.php 1791 2006-08-23 21:17:06Z fabien $
*/
class pakeNumberCompare
{
private $test = '';
public function __construct($test)
{
$this->test = $test;
}
public function test($number)
{
if (!preg_match('{^([<>]=?)?(.*?)([kmg]i?)?$}i', $this->test, $matches))
{
throw new pakeException(sprintf('Don\'t understand "%s" as a test.', $this->test));
}
$target = array_key_exists(2, $matches) ? $matches[2] : '';
$magnitude = array_key_exists(3, $matches) ? $matches[3] : '';
if (strtolower($magnitude) == 'k') $target *= 1000;
if (strtolower($magnitude) == 'ki') $target *= 1024;
if (strtolower($magnitude) == 'm') $target *= 1000000;
if (strtolower($magnitude) == 'mi') $target *= 1024*1024;
if (strtolower($magnitude) == 'g') $target *= 1000000000;
if (strtolower($magnitude) == 'gi') $target *= 1024*1024*1024;
$comparison = array_key_exists(1, $matches) ? $matches[1] : '==';
if ($comparison == '==' || $comparison == '')
{
return ($number == $target);
}
else if ($comparison == '>')
{
return ($number > $target);
}
else if ($comparison == '>=')
{
return ($number >= $target);
}
else if ($comparison == '<')
{
return ($number < $target);
}
else if ($comparison == '<=')
{
return ($number <= $target);
}
return false;
}
}
/*
=head1 SYNOPSIS
Number::Compare->new(">1Ki")->test(1025); # is 1025 > 1024
my $c = Number::Compare->new(">1M");
$c->(1_200_000); # slightly terser invocation
=head1 DESCRIPTION
=head1 METHODS
=head2 ->new( $test )
Returns a new object that compares the specified test.
=head2 ->test( $value )
A longhanded version of $compare->( $value ). Predates blessed
subroutine reference implementation.
=head2 ->parse_to_perl( $test )
Returns a perl code fragment equivalent to the test.
*/

310
lib/symfony/vendor/pake/pakeTask.class.php vendored Executable file
View File

@ -0,0 +1,310 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeTask.class.php 4358 2007-06-25 10:04:03Z fabien $
*/
/**
*
* .
*
* .
*
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeTask.class.php 4358 2007-06-25 10:04:03Z fabien $
*/
class pakeTask
{
protected static $TASKS = array();
protected static $ALIAS = array();
protected static $last_comment = '';
protected $prerequisites = array();
protected $name = '';
protected $comment = '';
protected $already_invoked = false;
protected $trace = null;
protected $verbose = null;
protected $dryrun = null;
protected $alias = '';
public function __construct($task_name)
{
$this->name = $task_name;
$this->comment = '';
$this->prerequisites = array();
$this->already_invoked = false;
$pake = pakeApp::get_instance();
$this->trace = $pake->get_trace();
$this->dryrun = $pake->get_dryrun();
$this->verbose = $pake->get_verbose();
}
public function is_verbose()
{
return $this->verbose;
}
public function enhance($deps = null)
{
if (!$deps) return;
if (is_array($deps))
{
$this->prerequisites = array_merge($this->prerequisites, $deps);
}
else
{
$this->prerequisites[] = $deps;
}
}
public static function get_tasks()
{
$tasks = pakeTask::$TASKS;
// we merge tasks and aliases
foreach (pakeTask::$ALIAS as $alias => $name)
{
if (!array_key_exists($name, $tasks))
{
throw new pakeException(sprintf('Task "%s" cannot be cloned to "%s" because it does not exist.', $name, $alias));
}
$alias_task = clone $tasks[$name];
$alias_task->alias = $name;
$alias_task->name = $alias;
$tasks[$alias] = $alias_task;
}
return $tasks;
}
public function get_property($name, $section = null)
{
$properties = pakeApp::get_instance()->get_properties();
if ($section)
{
if (!array_key_exists($section, $properties) || !array_key_exists($name, $properties[$section]))
{
throw new pakeException(sprintf('Property "%s/%s" does not exist.', $section, $name));
}
else
{
return $properties[$section][$name];
}
}
else
{
if (!array_key_exists($name, $properties))
{
throw new pakeException(sprintf('Property "%s" does not exist.', $name));
}
else
{
return $properties[$name];
}
}
}
public function get_alias()
{
return $this->alias;
}
public function get_prerequisites()
{
return $this->prerequisites;
}
public function get_name()
{
return $this->name;
}
public function get_comment()
{
return $this->comment;
}
// Format the trace flags for display.
private function format_trace_flags()
{
$flags = array();
if (!$this->already_invoked)
{
$flags[] = 'first_time';
}
if (!$this->is_needed())
{
$flags[] = 'not_needed';
}
return (count($flags)) ? '('.join(', ', $flags).')' : '';
}
public function invoke($args, $options)
{
if ($this->trace)
{
pake_echo_action('invoke', $this->name.' '.$this->format_trace_flags());
}
// return if already invoked
if ($this->already_invoked) return;
$this->already_invoked = true;
// run prerequisites
$tasks = self::get_tasks();
foreach ($this->prerequisites as $prerequisite)
{
$real_prerequisite = self::get_full_task_name($prerequisite);
if (array_key_exists($real_prerequisite, $tasks))
{
$tasks[$real_prerequisite]->invoke($args, $options);
}
else
{
throw new pakeException(sprintf('Prerequisite "%s" does not exist.', $prerequisite));
}
}
// only run if needed
if ($this->is_needed())
{
return $this->execute($args, $options);
}
}
public function execute($args, $options)
{
if ($this->dryrun)
{
pake_echo_action('execute', '(dry run) '.$this->name);
return;
}
if ($this->trace)
{
pake_echo_action('execute', $this->name);
}
// action to run
$function = ($this->get_alias() ? $this->get_alias() : $this->get_name());
if ($pos = strpos($function, '::'))
{
$function = array(substr($function, 0, $pos), preg_replace('/\-/', '_', 'run_'.strtolower(substr($function, $pos + 2))));
if (!is_callable($function))
{
throw new pakeException(sprintf('Task "%s" is defined but with no action defined.', $function[1]));
}
}
else
{
$function = preg_replace('/\-/', '_', 'run_'.strtolower($function));
if (!function_exists($function))
{
throw new pakeException(sprintf('Task "%s" is defined but with no action defined.', $this->name));
}
}
// execute action
return call_user_func_array($function, array($this, $args, $options));
}
public function is_needed()
{
return true;
}
public function timestamp()
{
$max = 0;
foreach ($this->prerequisites as $prerequisite)
{
$t = pakeTask::get($prerequisite)->timestamp();
if ($t > $max) $max = $t;
}
return ($max ? $max : time());
}
public static function define_task($name, $deps = null)
{
$task = pakeTask::lookup($name, 'pakeTask');
$task->add_comment();
$task->enhance($deps);
}
public static function define_alias($alias, $name)
{
self::$ALIAS[$alias] = $name;
}
public static function lookup($task_name, $class = 'pakeTask')
{
$tasks = self::get_tasks();
$task_name = self::get_full_task_name($task_name);
if (!array_key_exists($task_name, $tasks))
{
pakeTask::$TASKS[$task_name] = new $class($task_name);
}
return pakeTask::$TASKS[$task_name];
}
public static function get($task_name)
{
$tasks = self::get_tasks();
$task_name = self::get_full_task_name($task_name);
if (!array_key_exists($task_name, $tasks))
{
throw new pakeException(sprintf('Task "%s" is not defined.', $task_name));
}
return $tasks[$task_name];
}
public static function get_full_task_name($task_name)
{
foreach (self::get_tasks() as $task)
{
$mini_task_name = self::get_mini_task_name($task->get_name());
if ($mini_task_name == $task_name)
{
return $task->get_name();
}
}
return $task_name;
}
public static function get_mini_task_name($task_name)
{
$is_method_task = strpos($task_name, '::');
return ($is_method_task ? substr($task_name, $is_method_task + 2) : $task_name);
}
public static function define_comment($comment)
{
pakeTask::$last_comment = $comment;
}
public function add_comment()
{
if (!pakeTask::$last_comment) return;
if ($this->comment)
{
$this->comment .= ' / ';
}
$this->comment .= pakeTask::$last_comment;
pakeTask::$last_comment = '';
}
}

890
lib/symfony/vendor/pake/pakeYaml.class.php vendored Executable file
View File

@ -0,0 +1,890 @@
<?php
/**
* @package pake
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @license see the LICENSE file included in the distribution
* @version SVN: $Id: pakeYaml.class.php 2978 2006-12-08 19:15:44Z fabien $
*/
class pakeYaml
{
public static function load($input)
{
// syck is prefered over spyc
if (function_exists('syck_load')) {
if (!empty($input) && is_readable($input))
{
$input = file_get_contents($input);
}
return syck_load($input);
}
else
{
$spyc = new pakeSpyc();
return $spyc->load($input);
}
}
public static function dump($array)
{
$spyc = new pakeSpyc();
return $spyc->dump($array);
}
}
/**
* Spyc -- A Simple PHP YAML Class
* @version 0.2.2 -- 2006-01-29
* @author Chris Wanstrath <chris@ozmm.org>
* @link http://spyc.sourceforge.net/
* @copyright Copyright 2005-2006 Chris Wanstrath
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @package Spyc
*/
/**
* A node, used by Spyc for parsing YAML.
* @package Spyc
*/
class pakeYAMLNode {
/**#@+
* @access public
* @var string
*/
public $parent;
public $id;
/**#@+*/
/**
* @access public
* @var mixed
*/
public $data;
/**
* @access public
* @var int
*/
public $indent;
/**
* @access public
* @var bool
*/
public $children = false;
/**
* The constructor assigns the node a unique ID.
* @access public
* @return void
*/
public function pakeYAMLNode() {
$this->id = uniqid('');
}
}
/**
* The Simple PHP YAML Class.
*
* This class can be used to read a YAML file and convert its contents
* into a PHP array. It currently supports a very limited subsection of
* the YAML spec.
*
* Usage:
* <code>
* $parser = new Spyc;
* $array = $parser->load($file);
* </code>
* @package Spyc
*/
class pakeSpyc {
/**
* Load YAML into a PHP array statically
*
* The load method, when supplied with a YAML stream (string or file),
* will do its best to convert YAML in a file into a PHP array. Pretty
* simple.
* Usage:
* <code>
* $array = Spyc::YAMLLoad('lucky.yml');
* print_r($array);
* </code>
* @access public
* @return array
* @param string $input Path of YAML file or string containing YAML
*/
public function YAMLLoad($input) {
$spyc = new pakeSpyc;
return $spyc->load($input);
}
/**
* Dump YAML from PHP array statically
*
* The dump method, when supplied with an array, will do its best
* to convert the array into friendly YAML. Pretty simple. Feel free to
* save the returned string as nothing.yml and pass it around.
*
* Oh, and you can decide how big the indent is and what the wordwrap
* for folding is. Pretty cool -- just pass in 'false' for either if
* you want to use the default.
*
* Indent's default is 2 spaces, wordwrap's default is 40 characters. And
* you can turn off wordwrap by passing in 0.
*
* @access public
* @return string
* @param array $array PHP array
* @param int $indent Pass in false to use the default, which is 2
* @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
*/
public function YAMLDump($array,$indent = false,$wordwrap = false) {
$spyc = new pakeSpyc;
return $spyc->dump($array,$indent,$wordwrap);
}
/**
* Load YAML into a PHP array from an instantiated object
*
* The load method, when supplied with a YAML stream (string or file path),
* will do its best to convert the YAML into a PHP array. Pretty simple.
* Usage:
* <code>
* $parser = new Spyc;
* $array = $parser->load('lucky.yml');
* print_r($array);
* </code>
* @access public
* @return array
* @param string $input Path of YAML file or string containing YAML
*/
public function load($input) {
// See what type of input we're talking about
// If it's not a file, assume it's a string
if (!empty($input) && (strpos($input, "\n") === false)
&& file_exists($input)) {
$yaml = file($input);
} else {
$yaml = explode("\n",$input);
}
// Initiate some objects and values
$base = new pakeYAMLNode;
$base->indent = 0;
$this->_lastIndent = 0;
$this->_lastNode = $base->id;
$this->_inBlock = false;
$this->_isInline = false;
foreach ($yaml as $linenum => $line) {
$ifchk = trim($line);
// If the line starts with a tab (instead of a space), throw a fit.
if (preg_match('/^(\t)+(\w+)/', $line)) {
$err = 'ERROR: Line '. ($linenum + 1) .' in your input YAML begins'.
' with a tab. YAML only recognizes spaces. Please reformat.';
throw new Exception($err);
}
if ($this->_inBlock === false && empty($ifchk)) {
continue;
} elseif ($this->_inBlock == true && empty($ifchk)) {
$last =& $this->_allNodes[$this->_lastNode];
$last->data[key($last->data)] .= "\n";
} elseif ($ifchk{0} != '#' && substr($ifchk,0,3) != '---') {
// Create a new node and get its indent
$node = new pakeYAMLNode;
$node->indent = $this->_getIndent($line);
// Check where the node lies in the hierarchy
if ($this->_lastIndent == $node->indent) {
// If we're in a block, add the text to the parent's data
if ($this->_inBlock === true) {
$parent =& $this->_allNodes[$this->_lastNode];
$parent->data[key($parent->data)] .= trim($line).$this->_blockEnd;
} else {
// The current node's parent is the same as the previous node's
if (isset($this->_allNodes[$this->_lastNode])) {
$node->parent = $this->_allNodes[$this->_lastNode]->parent;
}
}
} elseif ($this->_lastIndent < $node->indent) {
if ($this->_inBlock === true) {
$parent =& $this->_allNodes[$this->_lastNode];
$parent->data[key($parent->data)] .= trim($line).$this->_blockEnd;
} elseif ($this->_inBlock === false) {
// The current node's parent is the previous node
$node->parent = $this->_lastNode;
// If the value of the last node's data was > or | we need to
// start blocking i.e. taking in all lines as a text value until
// we drop our indent.
$parent =& $this->_allNodes[$node->parent];
$this->_allNodes[$node->parent]->children = true;
if (is_array($parent->data)) {
$chk = $parent->data[key($parent->data)];
if ($chk === '>') {
$this->_inBlock = true;
$this->_blockEnd = ' ';
$parent->data[key($parent->data)] =
str_replace('>','',$parent->data[key($parent->data)]);
$parent->data[key($parent->data)] .= trim($line).' ';
$this->_allNodes[$node->parent]->children = false;
$this->_lastIndent = $node->indent;
} elseif ($chk === '|') {
$this->_inBlock = true;
$this->_blockEnd = "\n";
$parent->data[key($parent->data)] =
str_replace('|','',$parent->data[key($parent->data)]);
$parent->data[key($parent->data)] .= trim($line)."\n";
$this->_allNodes[$node->parent]->children = false;
$this->_lastIndent = $node->indent;
}
}
}
} elseif ($this->_lastIndent > $node->indent) {
// Any block we had going is dead now
if ($this->_inBlock === true) {
$this->_inBlock = false;
if ($this->_blockEnd = "\n") {
$last =& $this->_allNodes[$this->_lastNode];
$last->data[key($last->data)] =
trim($last->data[key($last->data)]);
}
}
// We don't know the parent of the node so we have to find it
// foreach ($this->_allNodes as $n) {
foreach ($this->_indentSort[$node->indent] as $n) {
if ($n->indent == $node->indent) {
$node->parent = $n->parent;
}
}
}
if ($this->_inBlock === false) {
// Set these properties with information from our current node
$this->_lastIndent = $node->indent;
// Set the last node
$this->_lastNode = $node->id;
// Parse the YAML line and return its data
$node->data = $this->_parseLine($line);
// Add the node to the master list
$this->_allNodes[$node->id] = $node;
// Add a reference to the node in an indent array
$this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id];
// Add a reference to the node in a References array if this node
// has a YAML reference in it.
if (
( (is_array($node->data)) &&
isset($node->data[key($node->data)]) &&
(!is_array($node->data[key($node->data)])) )
&&
( (preg_match('/^&([^ ]+)/',$node->data[key($node->data)]))
||
(preg_match('/^\*([^ ]+)/',$node->data[key($node->data)])) )
) {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
} elseif (
( (is_array($node->data)) &&
isset($node->data[key($node->data)]) &&
(is_array($node->data[key($node->data)])) )
) {
// Incomplete reference making code. Ugly, needs cleaned up.
foreach ($node->data[key($node->data)] as $d) {
if ( !is_array($d) &&
( (preg_match('/^&([^ ]+)/',$d))
||
(preg_match('/^\*([^ ]+)/',$d)) )
) {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
}
}
}
}
}
}
unset($node);
// Here we travel through node-space and pick out references (& and *)
$this->_linkReferences();
// Build the PHP array out of node-space
$trunk = $this->_buildArray();
return $trunk;
}
/**
* Dump PHP array to YAML
*
* The dump method, when supplied with an array, will do its best
* to convert the array into friendly YAML. Pretty simple. Feel free to
* save the returned string as tasteful.yml and pass it around.
*
* Oh, and you can decide how big the indent is and what the wordwrap
* for folding is. Pretty cool -- just pass in 'false' for either if
* you want to use the default.
*
* Indent's default is 2 spaces, wordwrap's default is 40 characters. And
* you can turn off wordwrap by passing in 0.
*
* @access public
* @return string
* @param array $array PHP array
* @param int $indent Pass in false to use the default, which is 2
* @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
*/
public function dump($array,$indent = false,$wordwrap = false) {
// Dumps to some very clean YAML. We'll have to add some more features
// and options soon. And better support for folding.
// New features and options.
if ($indent === false or !is_numeric($indent)) {
$this->_dumpIndent = 2;
} else {
$this->_dumpIndent = $indent;
}
if ($wordwrap === false or !is_numeric($wordwrap)) {
$this->_dumpWordWrap = 40;
} else {
$this->_dumpWordWrap = $wordwrap;
}
// New YAML document
$string = "---\n";
// Start at the base of the array and move through it.
foreach ($array as $key => $value) {
$string .= $this->_yamlize($key,$value,0);
}
return $string;
}
/**** Private Properties ****/
/**#@+
* @access private
* @var mixed
*/
private $_haveRefs;
private $_allNodes;
private $_lastIndent;
private $_lastNode;
private $_inBlock;
private $_isInline;
private $_dumpIndent;
private $_dumpWordWrap;
/**#@+*/
/**** Private Methods ****/
/**
* Attempts to convert a key / value array item to YAML
* @access private
* @return string
* @param $key The name of the key
* @param $value The value of the item
* @param $indent The indent of the current node
*/
private function _yamlize($key,$value,$indent) {
if (is_array($value)) {
// It has children. What to do?
// Make it the right kind of item
$string = $this->_dumpNode($key,NULL,$indent);
// Add the indent
$indent += $this->_dumpIndent;
// Yamlize the array
$string .= $this->_yamlizeArray($value,$indent);
} elseif (!is_array($value)) {
// It doesn't have children. Yip.
$string = $this->_dumpNode($key,$value,$indent);
}
return $string;
}
/**
* Attempts to convert an array to YAML
* @access private
* @return string
* @param $array The array you want to convert
* @param $indent The indent of the current level
*/
private function _yamlizeArray($array,$indent) {
if (is_array($array)) {
$string = '';
foreach ($array as $key => $value) {
$string .= $this->_yamlize($key,$value,$indent);
}
return $string;
} else {
return false;
}
}
/**
* Returns YAML from a key and a value
* @access private
* @return string
* @param $key The name of the key
* @param $value The value of the item
* @param $indent The indent of the current node
*/
private function _dumpNode($key,$value,$indent) {
// do some folding here, for blocks
if (strpos($value,"\n")) {
$value = $this->_doLiteralBlock($value,$indent);
} else {
$value = $this->_doFolding($value,$indent);
}
$spaces = str_repeat(' ',$indent);
if (is_int($key)) {
// It's a sequence
$string = $spaces.'- '.$value."\n";
} else {
// It's mapped
$string = $spaces.$key.': '.$value."\n";
}
return $string;
}
/**
* Creates a literal block for dumping
* @access private
* @return string
* @param $value
* @param $indent int The value of the indent
*/
private function _doLiteralBlock($value,$indent) {
$exploded = explode("\n",$value);
$newValue = '|';
$indent += $this->_dumpIndent;
$spaces = str_repeat(' ',$indent);
foreach ($exploded as $line) {
$newValue .= "\n" . $spaces . trim($line);
}
return $newValue;
}
/**
* Folds a string of text, if necessary
* @access private
* @return string
* @param $value The string you wish to fold
*/
private function _doFolding($value,$indent) {
// Don't do anything if wordwrap is set to 0
if ($this->_dumpWordWrap === 0) {
return $value;
}
if (strlen($value) > $this->_dumpWordWrap) {
$indent += $this->_dumpIndent;
$indent = str_repeat(' ',$indent);
$wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
$value = ">\n".$indent.$wrapped;
}
return $value;
}
/* Methods used in loading */
/**
* Finds and returns the indentation of a YAML line
* @access private
* @return int
* @param string $line A line from the YAML file
*/
private function _getIndent($line) {
preg_match('/^\s{1,}/',$line,$match);
if (!empty($match[0])) {
$indent = substr_count($match[0],' ');
} else {
$indent = 0;
}
return $indent;
}
/**
* Parses YAML code and returns an array for a node
* @access private
* @return array
* @param string $line A line from the YAML file
*/
private function _parseLine($line) {
$line = trim($line);
$array = array();
if (preg_match('/^-(.*):$/',$line)) {
// It's a mapped sequence
$key = trim(substr(substr($line,1),0,-1));
$array[$key] = '';
} elseif ($line[0] == '-' && substr($line,0,3) != '---') {
// It's a list item but not a new stream
if (strlen($line) > 1) {
$value = trim(substr($line,1));
// Set the type of the value. Int, string, etc
$value = $this->_toType($value);
$array[] = $value;
} else {
$array[] = array();
}
} elseif (preg_match('/^(.+):/',$line,$key)) {
// It's a key/value pair most likely
// If the key is in double quotes pull it out
if (preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
$value = trim(str_replace($matches[1],'',$line));
$key = $matches[2];
} else {
// Do some guesswork as to the key and the value
$explode = explode(':',$line);
$key = trim($explode[0]);
array_shift($explode);
$value = trim(implode(':',$explode));
}
// Set the type of the value. Int, string, etc
$value = $this->_toType($value);
if (empty($key)) {
$array[] = $value;
} else {
$array[$key] = $value;
}
}
return $array;
}
/**
* Finds the type of the passed value, returns the value as the new type.
* @access private
* @param string $value
* @return mixed
*/
private function _toType($value) {
if (preg_match('/^("(.*)"|\'(.*)\')/',$value,$matches)) {
$value = (string)preg_replace('/(\'\'|\\\\\')/',"'",end($matches));
$value = preg_replace('/\\\\"/','"',$value);
} elseif (preg_match('/^\\[(.+)\\]$/',$value,$matches)) {
// Inline Sequence
// Take out strings sequences and mappings
$explode = $this->_inlineEscape($matches[1]);
// Propogate value array
$value = array();
foreach ($explode as $v) {
$value[] = $this->_toType($v);
}
} elseif (strpos($value,': ')!==false && !preg_match('/^{(.+)/',$value)) {
// It's a map
$array = explode(': ',$value);
$key = trim($array[0]);
array_shift($array);
$value = trim(implode(': ',$array));
$value = $this->_toType($value);
$value = array($key => $value);
} elseif (preg_match("/{(.+)}$/",$value,$matches)) {
// Inline Mapping
// Take out strings sequences and mappings
$explode = $this->_inlineEscape($matches[1]);
// Propogate value array
$array = array();
foreach ($explode as $v) {
$array = $array + $this->_toType($v);
}
$value = $array;
} elseif (strtolower($value) == 'null' or $value == '' or $value == '~') {
$value = NULL;
} elseif (ctype_digit($value)) {
$value = (int)$value;
} elseif (in_array(strtolower($value),
array('true', 'on', '+', 'yes', 'y'))) {
$value = TRUE;
} elseif (in_array(strtolower($value),
array('false', 'off', '-', 'no', 'n'))) {
$value = FALSE;
} elseif (is_numeric($value)) {
$value = (float)$value;
} else {
// Just a normal string, right?
$value = trim(preg_replace('/#(.+)$/','',$value));
}
return $value;
}
/**
* Used in inlines to check for more inlines or quoted strings
* @access private
* @return array
*/
private function _inlineEscape($inline) {
// There's gotta be a cleaner way to do this...
// While pure sequences seem to be nesting just fine,
// pure mappings and mappings with sequences inside can't go very
// deep. This needs to be fixed.
// Check for strings
$regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
if (preg_match_all($regex,$inline,$strings)) {
$strings = $strings[2];
$inline = preg_replace($regex,'YAMLString',$inline);
}
unset($regex);
// Check for sequences
if (preg_match_all('/\[(.+)\]/U',$inline,$seqs)) {
$inline = preg_replace('/\[(.+)\]/U','YAMLSeq',$inline);
$seqs = $seqs[0];
}
// Check for mappings
if (preg_match_all('/{(.+)}/U',$inline,$maps)) {
$inline = preg_replace('/{(.+)}/U','YAMLMap',$inline);
$maps = $maps[0];
}
$explode = explode(', ',$inline);
// Re-add the strings
if (!empty($strings)) {
$i = 0;
foreach ($explode as $key => $value) {
if ($value == 'YAMLString') {
$explode[$key] = $strings[$i];
++$i;
}
}
}
// Re-add the sequences
if (!empty($seqs)) {
$i = 0;
foreach ($explode as $key => $value) {
if (strpos($value,'YAMLSeq') !== false) {
$explode[$key] = str_replace('YAMLSeq',$seqs[$i],$value);
++$i;
}
}
}
// Re-add the mappings
if (!empty($maps)) {
$i = 0;
foreach ($explode as $key => $value) {
if (strpos($value,'YAMLMap') !== false) {
$explode[$key] = str_replace('YAMLMap',$maps[$i],$value);
++$i;
}
}
}
return $explode;
}
/**
* Builds the PHP array from all the YAML nodes we've gathered
* @access private
* @return array
*/
private function _buildArray() {
$trunk = array();
if (!isset($this->_indentSort[0])) {
return $trunk;
}
foreach ($this->_indentSort[0] as $n) {
if (empty($n->parent)) {
$this->_nodeArrayizeData($n);
// Check for references and copy the needed data to complete them.
$this->_makeReferences($n);
// Merge our data with the big array we're building
$trunk = $this->_array_kmerge($trunk,$n->data);
}
}
return $trunk;
}
/**
* Traverses node-space and sets references (& and *) accordingly
* @access private
* @return bool
*/
private function _linkReferences() {
if (is_array($this->_haveRefs)) {
foreach ($this->_haveRefs as $node) {
if (!empty($node->data)) {
$key = key($node->data);
// If it's an array, don't check.
if (is_array($node->data[$key])) {
foreach ($node->data[$key] as $k => $v) {
$this->_linkRef($node,$key,$k,$v);
}
} else {
$this->_linkRef($node,$key);
}
}
}
}
return true;
}
function _linkRef(&$n,$key,$k = NULL,$v = NULL) {
if (empty($k) && empty($v)) {
// Look for &refs
if (preg_match('/^&([^ ]+)/',$n->data[$key],$matches)) {
// Flag the node so we know it's a reference
$this->_allNodes[$n->id]->ref = substr($matches[0],1);
$this->_allNodes[$n->id]->data[$key] =
substr($n->data[$key],strlen($matches[0])+1);
// Look for *refs
} elseif (preg_match('/^\*([^ ]+)/',$n->data[$key],$matches)) {
$ref = substr($matches[0],1);
// Flag the node as having a reference
$this->_allNodes[$n->id]->refKey = $ref;
}
} elseif (!empty($k) && !empty($v)) {
if (preg_match('/^&([^ ]+)/',$v,$matches)) {
// Flag the node so we know it's a reference
$this->_allNodes[$n->id]->ref = substr($matches[0],1);
$this->_allNodes[$n->id]->data[$key][$k] =
substr($v,strlen($matches[0])+1);
// Look for *refs
} elseif (preg_match('/^\*([^ ]+)/',$v,$matches)) {
$ref = substr($matches[0],1);
// Flag the node as having a reference
$this->_allNodes[$n->id]->refKey = $ref;
}
}
}
/**
* Finds the children of a node and aids in the building of the PHP array
* @access private
* @param int $nid The id of the node whose children we're gathering
* @return array
*/
private function _gatherChildren($nid) {
$return = array();
$node =& $this->_allNodes[$nid];
foreach ($this->_allNodes as $z) {
if ($z->parent == $node->id) {
// We found a child
$this->_nodeArrayizeData($z);
// Check for references
$this->_makeReferences($z);
// Merge with the big array we're returning
// The big array being all the data of the children of our parent node
$return = $this->_array_kmerge($return,$z->data);
}
}
return $return;
}
/**
* Turns a node's data and its children's data into a PHP array
*
* @access private
* @param array $node The node which you want to arrayize
* @return boolean
*/
private function _nodeArrayizeData(&$node) {
if (is_array($node->data) && $node->children == true) {
// This node has children, so we need to find them
$childs = $this->_gatherChildren($node->id);
// We've gathered all our children's data and are ready to use it
$key = key($node->data);
$key = empty($key) ? 0 : $key;
// If it's an array, add to it of course
if (is_array($node->data[$key])) {
$node->data[$key] = $this->_array_kmerge($node->data[$key],$childs);
} else {
$node->data[$key] = $childs;
}
} elseif (!is_array($node->data) && $node->children == true) {
// Same as above, find the children of this node
$childs = $this->_gatherChildren($node->id);
$node->data = array();
$node->data[] = $childs;
}
// We edited $node by reference, so just return true
return true;
}
/**
* Traverses node-space and copies references to / from this object.
* @access private
* @param object $z A node whose references we wish to make real
* @return bool
*/
private function _makeReferences(&$z) {
// It is a reference
if (isset($z->ref)) {
$key = key($z->data);
// Copy the data to this object for easy retrieval later
$this->ref[$z->ref] =& $z->data[$key];
// It has a reference
} elseif (isset($z->refKey)) {
if (isset($this->ref[$z->refKey])) {
$key = key($z->data);
// Copy the data from this object to make the node a real reference
$z->data[$key] =& $this->ref[$z->refKey];
}
}
return true;
}
/**
* Merges arrays and maintains numeric keys.
*
* An ever-so-slightly modified version of the array_kmerge() function posted
* to php.net by mail at nospam dot iaindooley dot com on 2004-04-08.
*
* http://us3.php.net/manual/en/function.array-merge.php#41394
*
* @access private
* @param array $arr1
* @param array $arr2
* @return array
*/
private function _array_kmerge($arr1,$arr2) {
if(!is_array($arr1))
$arr1 = array();
if(!is_array($arr2))
$arr2 = array();
$keys1 = array_keys($arr1);
$keys2 = array_keys($arr2);
$keys = array_merge($keys1,$keys2);
$vals1 = array_values($arr1);
$vals2 = array_values($arr2);
$vals = array_merge($vals1,$vals2);
$ret = array();
foreach($keys as $key) {
list($unused,$val) = each($vals);
// This is the good part! If a key already exists, but it's part of a
// sequence (an int), just keep addin numbers until we find a fresh one.
if (isset($ret[$key]) and is_int($key)) {
while (array_key_exists($key, $ret)) {
$key++;
}
}
$ret[$key] = $val;
}
return $ret;
}
}

Some files were not shown because too many files have changed in this diff Show More