2010-12-02 23:34:41 +00:00
< ? 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' ;
2022-05-26 14:56:23 +00:00
use ProcessMaker\Core\System ;
2020-05-06 23:38:00 -04:00
2010-12-02 23:34:41 +00:00
/**
* 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 );
}
2016-12-20 12:35:10 -04:00
if ( ! class_exists ( 'G' )) {
$realdocuroot = str_replace ( '\\' , '/' , $_SERVER [ 'DOCUMENT_ROOT' ]);
$docuroot = explode ( '/' , $realdocuroot );
array_pop ( $docuroot );
$pathhome = implode ( '/' , $docuroot ) . '/' ;
array_pop ( $docuroot );
$pathTrunk = implode ( '/' , $docuroot ) . '/' ;
require_once ( $pathTrunk . 'gulliver/system/class.g.php' );
2015-04-08 17:35:40 -04:00
}
2010-12-02 23:34:41 +00:00
// sort $dsninfo by keys so the serialized result is always the same
// for identical connection parameters, no matter what their order is
ksort ( $dsninfo );
2015-04-08 17:35:40 -04:00
$connectionMapKey = G :: encryptCrc32 ( serialize ( $dsninfo + array ( 'compat_flags' => ( $flags & Creole :: COMPAT_ALL ))));
2010-12-02 23:34:41 +00:00
// see if we already have a connection with these parameters cached
if ( isset ( self :: $connectionMap [ $connectionMapKey ]) && $dsninfo [ 'phptype' ] !== 'dbarray' )
{
// 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 ) {
2015-04-02 11:56:46 -04:00
$sqle -> setUserInfo (( isset ( $dsninfo [ " username " ])) ? $dsninfo [ " username " ] : " " );
2010-12-02 23:34:41 +00:00
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 );
2020-05-06 23:38:00 -04:00
if ( $info === false ) {
$info = System :: parseUrlWithNotEncodedPassword ( $dsn );
}
2017-08-02 16:06:56 -04:00
$info [ 'pass' ] = urldecode ( $info [ 'pass' ]);
2010-12-02 23:34:41 +00:00
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
2024-01-18 11:52:42 -04:00
$parsed [ 'phptype' ] = $info [ 'scheme' ] ? ? '' ;
$parsed [ 'username' ] = $info [ 'user' ] ? ? '' ;
$parsed [ 'password' ] = $info [ 'pass' ] ? ? '' ;
$parsed [ 'port' ] = $info [ 'port' ] ? ? '' ;
2010-12-02 23:34:41 +00:00
2024-01-18 11:52:42 -04:00
$host = $info [ 'host' ] ? ? '' ;
2010-12-02 23:34:41 +00:00
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 ;
}
}