2006-05-30 12:42:10 +04:00
< ? php
2006-08-23 00:14:29 +04:00
/*
2006-07-27 21:51:19 +04:00
* $Id $
*
* 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 , see
* < http :// www . phpdoctrine . com >.
*/
2006-11-11 23:00:30 +03:00
/**
* Doctrine_Db
* A thin wrapper layer on top of PDO / Doctrine_Adapter
*
2006-11-13 21:16:10 +03:00
* Doctrine_Db provides the following things to underlying database hanlder
2006-11-11 23:00:30 +03:00
*
2006-11-13 21:16:10 +03:00
* 1. Event listeners
* An easy to use , pluggable eventlistener architecture . Aspects such as
* logging , query profiling and caching can be easily implemented through
* the use of these listeners
2006-11-11 23:00:30 +03:00
*
2006-11-13 21:16:10 +03:00
* 2. Lazy - connecting
* Creating an instance of Doctrine_Db does not connect
* to database . Connecting to database is only invoked when actually needed
* ( for example when query () is being called )
*
* 3. Portable error codes
* Doctrine_Db_Exception drivers provide portable error code handling .
*
* 4. Easy - to - use fetching methods
* For convience Doctrine_Db provides methods such as fetchOne (), fetchAssoc () etc .
*
* @ author Konsta Vesterinen < kvesteri @ cc . hut . fi >
* @ license http :// www . opensource . org / licenses / lgpl - license . php LGPL
* @ package Doctrine
* @ category Object Relational Mapping
* @ link www . phpdoctrine . com
* @ since 1.0
* @ version $Revision $
*/
2006-12-29 17:40:47 +03:00
class Doctrine_Db implements Countable , IteratorAggregate , Doctrine_Adapter_Interface
{
2006-11-11 23:00:30 +03:00
/**
* @ var array $instances all the instances of this class
*/
protected static $instances = array ();
2006-05-30 12:42:10 +04:00
/**
2006-11-11 23:00:30 +03:00
* @ var array $isConnected whether or not a connection has been established
2006-05-30 12:42:10 +04:00
*/
2006-11-11 23:00:30 +03:00
protected $isConnected = false ;
2006-05-30 12:42:10 +04:00
/**
2006-11-11 23:00:30 +03:00
* @ var PDO $dbh the database handler
2006-05-30 12:42:10 +04:00
*/
2006-11-11 23:00:30 +03:00
protected $dbh ;
2006-05-30 12:42:10 +04:00
/**
2006-11-11 23:00:30 +03:00
* @ var array $options
2006-05-30 12:42:10 +04:00
*/
2006-11-11 23:00:30 +03:00
protected $options = array ( 'dsn' => null ,
'username' => null ,
'password' => null ,
);
/**
2006-12-29 17:01:31 +03:00
* @ var Doctrine_Db_EventListener_Interface | Doctrine_Overloadable $listener
2006-11-11 23:00:30 +03:00
* listener for listening events
*/
protected $listener ;
/**
* @ var integer $querySequence
*/
protected $querySequence = 0 ;
private static $driverMap = array ( 'oracle' => 'oci8' ,
'postgres' => 'pgsql' ,
'oci' => 'oci8' ,
'sqlite2' => 'sqlite' ,
'sqlite3' => 'sqlite' );
2006-05-30 12:42:10 +04:00
/**
* constructor
2006-11-11 23:00:30 +03:00
*
* @ param string $dsn data source name
* @ param string $user database username
* @ param string $pass database password
2006-05-30 12:42:10 +04:00
*/
2006-12-29 17:40:47 +03:00
public function __construct ( $dsn , $user , $pass )
{
2006-12-29 17:01:31 +03:00
if ( ! isset ( $user )) {
2006-11-11 23:00:30 +03:00
$a = self :: parseDSN ( $dsn );
extract ( $a );
}
$this -> options [ 'dsn' ] = $dsn ;
$this -> options [ 'username' ] = $user ;
$this -> options [ 'password' ] = $pass ;
$this -> listener = new Doctrine_Db_EventListener ();
2006-05-30 12:42:10 +04:00
}
2006-11-11 23:00:30 +03:00
2006-12-29 17:40:47 +03:00
public function nextQuerySequence ()
{
2006-11-11 23:00:30 +03:00
return ++ $this -> querySequence ;
}
/**
* getQuerySequence
*/
2006-12-29 17:40:47 +03:00
public function getQuerySequence ()
{
2006-11-11 23:00:30 +03:00
return $this -> querySequence ;
}
/**
* getDBH
*/
2006-12-29 17:40:47 +03:00
public function getDBH ()
{
2006-11-11 23:00:30 +03:00
return $this -> dbh ;
}
2006-12-29 17:40:47 +03:00
public function getOption ( $name )
{
2006-12-29 17:01:31 +03:00
if ( ! array_key_exists ( $name , $this -> options )) {
2006-11-11 23:00:30 +03:00
throw new Doctrine_Db_Exception ( 'Unknown option ' . $name );
2006-12-29 17:01:31 +03:00
}
2006-11-11 23:00:30 +03:00
return $this -> options [ $name ];
2006-05-30 12:42:10 +04:00
}
2006-11-11 23:00:30 +03:00
/**
* addListener
*
* @ param Doctrine_Db_EventListener_Interface | Doctrine_Overloadable $listener
* @ return Doctrine_Db
*/
2006-12-29 17:40:47 +03:00
public function addListener ( $listener , $name = null )
{
2006-12-29 17:01:31 +03:00
if ( ! ( $this -> listener instanceof Doctrine_Db_EventListener_Chain )) {
2006-11-11 23:00:30 +03:00
$this -> listener = new Doctrine_Db_EventListener_Chain ();
2006-12-29 17:01:31 +03:00
}
2006-11-11 23:00:30 +03:00
$this -> listener -> add ( $listener , $name );
2006-12-29 17:01:31 +03:00
2006-11-11 23:00:30 +03:00
return $this ;
}
/**
* getListener
2006-12-29 17:01:31 +03:00
*
2006-11-11 23:00:30 +03:00
* @ return Doctrine_Db_EventListener_Interface | Doctrine_Overloadable
*/
2006-12-29 17:40:47 +03:00
public function getListener ()
{
2006-11-11 23:00:30 +03:00
return $this -> listener ;
}
2006-05-30 12:42:10 +04:00
/**
2006-11-11 23:00:30 +03:00
* setListener
*
* @ param Doctrine_Db_EventListener_Interface | Doctrine_Overloadable $listener
* @ return Doctrine_Db
2006-05-30 12:42:10 +04:00
*/
2006-12-29 17:40:47 +03:00
public function setListener ( $listener )
{
2006-12-29 17:01:31 +03:00
if ( ! ( $listener instanceof Doctrine_Db_EventListener_Interface )
&& ! ( $listener instanceof Doctrine_Overloadable )
) {
2006-11-11 23:00:30 +03:00
throw new Doctrine_Db_Exception ( " Couldn't set eventlistener for database handler. EventListeners should implement either Doctrine_Db_EventListener_Interface or Doctrine_Overloadable " );
2006-12-29 17:01:31 +03:00
}
2006-11-11 23:00:30 +03:00
$this -> listener = $listener ;
2006-05-30 12:42:10 +04:00
2006-11-11 23:00:30 +03:00
return $this ;
}
/**
* connect
* connects into database
*
* @ return boolean
*/
2006-12-29 17:40:47 +03:00
public function connect ()
{
2006-12-29 17:01:31 +03:00
if ( $this -> isConnected )
2006-11-11 23:00:30 +03:00
return false ;
$this -> dbh = new PDO ( $this -> options [ 'dsn' ], $this -> options [ 'username' ], $this -> options [ 'password' ]);
$this -> dbh -> setAttribute ( PDO :: ATTR_ERRMODE , PDO :: ERRMODE_EXCEPTION );
$this -> dbh -> setAttribute ( PDO :: ATTR_STATEMENT_CLASS , array ( " Doctrine_Db_Statement " , array ( $this )));
$this -> isConnected = true ;
return true ;
}
/**
* getConnection
*
* @ param string $dsn PEAR :: DB like DSN or PDO like DSN
* format for PEAR :: DB like DSN : schema :// user : password @ address / dbname
*
* @ return
*/
2006-12-29 17:40:47 +03:00
public static function getConnection ( $dsn = null , $username = null , $password = null )
{
2006-11-11 23:00:30 +03:00
return new self ( $dsn , $username , $password );
}
/**
* driverName
2006-12-29 17:01:31 +03:00
* converts a driver name like ( oracle ) to appropriate PDO
2006-11-11 23:00:30 +03:00
* driver name ( oci8 in the case of oracle )
*
* @ param string $name
* @ return string
*/
2006-12-29 17:40:47 +03:00
public static function driverName ( $name )
{
2006-12-29 17:01:31 +03:00
if ( isset ( self :: $driverMap [ $name ])) {
2006-11-11 23:00:30 +03:00
return self :: $driverMap [ $name ];
2006-12-29 17:01:31 +03:00
}
2006-11-11 23:00:30 +03:00
return $name ;
}
/**
* parseDSN
*
2006-12-29 17:01:31 +03:00
* @ param string $dsn
* @ return array Parsed contents of DSN
2006-11-11 23:00:30 +03:00
*/
2006-12-29 17:40:47 +03:00
function parseDSN ( $dsn )
{
2006-11-11 23:00:30 +03:00
// silence any warnings
2006-12-29 17:01:31 +03:00
$parts = @ parse_url ( $dsn );
2006-11-11 23:00:30 +03:00
$names = array ( 'scheme' , 'host' , 'port' , 'user' , 'pass' , 'path' , 'query' , 'fragment' );
2006-12-29 17:01:31 +03:00
foreach ( $names as $name ) {
if ( ! isset ( $parts [ $name ])) {
2006-11-11 23:00:30 +03:00
$parts [ $name ] = null ;
2006-12-29 17:01:31 +03:00
}
2006-05-30 12:42:10 +04:00
}
2006-11-11 23:00:30 +03:00
2006-12-29 17:01:31 +03:00
if ( count ( $parts ) == 0 || ! isset ( $parts [ 'scheme' ])) {
throw new Doctrine_Db_Exception ( 'Empty data source name' );
}
2006-11-11 23:00:30 +03:00
$drivers = self :: getAvailableDrivers ();
$parts [ 'scheme' ] = self :: driverName ( $parts [ 'scheme' ]);
2006-12-29 17:01:31 +03:00
if ( ! in_array ( $parts [ 'scheme' ], $drivers )) {
2006-11-11 23:00:30 +03:00
throw new Doctrine_Db_Exception ( 'Driver ' . $parts [ 'scheme' ] . ' not availible or extension not loaded' );
2006-12-29 17:01:31 +03:00
}
switch ( $parts [ 'scheme' ]) {
case 'sqlite' :
if ( isset ( $parts [ 'host' ]) && $parts [ 'host' ] == ':memory' ) {
$parts [ 'database' ] = ':memory:' ;
$parts [ 'dsn' ] = 'sqlite::memory:' ;
}
2006-11-11 23:00:30 +03:00
break ;
2006-12-29 17:01:31 +03:00
case 'mysql' :
case 'informix' :
case 'oci8' :
case 'mssql' :
case 'firebird' :
case 'pgsql' :
case 'odbc' :
if ( ! isset ( $parts [ 'path' ]) || $parts [ 'path' ] == '/' ) {
throw new Doctrine_Db_Exception ( 'No database availible in data source name' );
}
if ( isset ( $parts [ 'path' ])) {
$parts [ 'database' ] = substr ( $parts [ 'path' ], 1 );
}
if ( ! isset ( $parts [ 'host' ])) {
throw new Doctrine_Db_Exception ( 'No hostname set in data source name' );
}
$parts [ 'dsn' ] = $parts [ " scheme " ] . " :host= " . $parts [ " host " ] . " ;dbname= " . $parts [ " database " ];
2006-11-11 23:00:30 +03:00
break ;
2006-12-29 17:01:31 +03:00
default :
throw new Doctrine_Db_Exception ( 'Unknown driver ' . $parts [ 'scheme' ]);
}
2006-11-11 23:00:30 +03:00
2006-12-29 17:01:31 +03:00
return $parts ;
}
2006-11-11 23:00:30 +03:00
/**
* clear
* clears all instances from the memory
*
* @ return void
*/
2006-12-29 17:40:47 +03:00
public static function clear ()
{
2006-11-11 23:00:30 +03:00
self :: $instances = array ();
}
/**
* errorCode
* Fetch the SQLSTATE associated with the last operation on the database handle
*
* @ return integer
*/
2006-12-29 17:40:47 +03:00
public function errorCode ()
{
2006-11-11 23:00:30 +03:00
return $this -> dbh -> errorCode ();
2006-05-30 12:42:10 +04:00
}
/**
2006-11-11 23:00:30 +03:00
* errorInfo
* Fetch extended error information associated with the last operation on the database handle
*
* @ return array
2006-05-30 12:42:10 +04:00
*/
2006-12-29 17:40:47 +03:00
public function errorInfo ()
{
2006-11-11 23:00:30 +03:00
return $this -> dbh -> errorInfo ();
}
/**
* prepare
*
* @ param string $statement
*/
2006-12-29 17:40:47 +03:00
public function prepare ( $statement )
{
2006-11-11 23:00:30 +03:00
$this -> connect ();
2006-05-30 12:42:10 +04:00
2006-11-11 23:00:30 +03:00
$event = new Doctrine_Db_Event ( $this , Doctrine_Db_Event :: PREPARE , $statement );
2006-05-30 12:42:10 +04:00
2006-11-11 23:00:30 +03:00
$this -> listener -> onPrePrepare ( $event );
$stmt = $this -> dbh -> prepare ( $statement );
$this -> listener -> onPrepare ( $event );
$this -> querySequence ++ ;
return $stmt ;
}
/**
* query
*
* @ param string $statement
* @ param array $params
* @ return Doctrine_Db_Statement | boolean
*/
public function query ( $statement , array $params = array ()) {
$this -> connect ();
2006-12-29 17:01:31 +03:00
2006-11-11 23:00:30 +03:00
$event = new Doctrine_Db_Event ( $this , Doctrine_Db_Event :: QUERY , $statement );
$this -> listener -> onPreQuery ( $event );
2006-12-29 17:01:31 +03:00
if ( ! empty ( $params )) {
2006-11-11 23:00:30 +03:00
$stmt = $this -> dbh -> query ( $statement ) -> execute ( $params );
2006-12-29 17:01:31 +03:00
} else {
2006-11-11 23:00:30 +03:00
$stmt = $this -> dbh -> query ( $statement );
2006-12-29 17:01:31 +03:00
}
2006-11-11 23:00:30 +03:00
$this -> listener -> onQuery ( $event );
$this -> querySequence ++ ;
return $stmt ;
2006-05-30 12:42:10 +04:00
}
/**
2006-11-11 23:00:30 +03:00
* quote
* quotes a string for use in a query
*
* @ param string $input
* @ return string
2006-05-30 12:42:10 +04:00
*/
2006-12-29 17:40:47 +03:00
public function quote ( $input )
{
2006-11-11 23:00:30 +03:00
$this -> connect ();
2006-06-01 15:58:05 +04:00
2006-11-11 23:00:30 +03:00
return $this -> dbh -> quote ( $input );
2006-05-30 12:42:10 +04:00
}
/**
2006-11-11 23:00:30 +03:00
* exec
* executes an SQL statement and returns the number of affected rows
*
* @ param string $statement
* @ return integer
*/
2006-12-29 17:40:47 +03:00
public function exec ( $statement )
{
2006-11-11 23:00:30 +03:00
$this -> connect ();
$args = func_get_args ();
2006-12-29 17:01:31 +03:00
2006-11-11 23:00:30 +03:00
$event = new Doctrine_Db_Event ( $this , Doctrine_Db_Event :: EXEC , $statement );
$this -> listener -> onPreExec ( $event );
$rows = $this -> dbh -> exec ( $statement );
$this -> listener -> onExec ( $event );
return $rows ;
}
2006-11-16 15:45:34 +03:00
2006-11-11 23:00:30 +03:00
/**
* lastInsertId
*
* @ return integer
2006-05-30 12:42:10 +04:00
*/
2006-12-29 17:40:47 +03:00
public function lastInsertId ()
{
2006-11-11 23:00:30 +03:00
$this -> connect ();
return $this -> dbh -> lastInsertId ();
2006-05-30 12:42:10 +04:00
}
2006-11-11 23:00:30 +03:00
/**
* begins a transaction
*
* @ return boolean
*/
2006-12-29 17:40:47 +03:00
public function beginTransaction ()
{
2006-11-11 23:00:30 +03:00
$event = new Doctrine_Db_Event ( $this , Doctrine_Db_Event :: BEGIN );
$this -> listener -> onPreBeginTransaction ( $event );
$return = $this -> dbh -> beginTransaction ();
$this -> listener -> onBeginTransaction ( $event );
2006-12-29 17:01:31 +03:00
2006-11-11 23:00:30 +03:00
return $return ;
2006-05-30 12:42:10 +04:00
}
/**
2006-11-11 23:00:30 +03:00
* commits a transaction
*
* @ return boolean
2006-05-30 12:42:10 +04:00
*/
2006-12-29 17:40:47 +03:00
public function commit ()
{
2006-11-11 23:00:30 +03:00
$event = new Doctrine_Db_Event ( $this , Doctrine_Db_Event :: COMMIT );
$this -> listener -> onPreCommit ( $event );
$return = $this -> dbh -> commit ();
$this -> listener -> onCommit ( $event );
return $return ;
2006-05-30 12:42:10 +04:00
}
/**
2006-11-11 23:00:30 +03:00
* rollBack
*
* @ return boolean
*/
2006-12-29 17:40:47 +03:00
public function rollBack ()
{
2006-11-11 23:00:30 +03:00
$this -> connect ();
$event = new Doctrine_Db_Event ( $this , Doctrine_Db_Event :: ROLLBACK );
$this -> listener -> onPreRollback ( $event );
$this -> dbh -> rollBack ();
2006-12-29 17:01:31 +03:00
2006-11-11 23:00:30 +03:00
$this -> listener -> onRollback ( $event );
}
/**
* getAttribute
* retrieves a database connection attribute
*
* @ param integer $attribute
* @ return mixed
*/
2006-12-29 17:40:47 +03:00
public function getAttribute ( $attribute )
{
2006-11-11 23:00:30 +03:00
$this -> connect ();
2006-12-29 17:01:31 +03:00
2006-11-11 23:00:30 +03:00
return $this -> dbh -> getAttribute ( $attribute );
}
/**
* returns an array of available PDO drivers
*/
2006-12-29 17:40:47 +03:00
public static function getAvailableDrivers ()
{
2006-11-11 23:00:30 +03:00
return PDO :: getAvailableDrivers ();
}
/**
* setAttribute
* sets an attribute
*
* @ param integer $attribute
* @ param mixed $value
* @ return boolean
*/
2006-12-29 17:40:47 +03:00
public function setAttribute ( $attribute , $value )
{
2006-11-11 23:00:30 +03:00
$this -> connect ();
2006-12-29 17:01:31 +03:00
2006-11-11 23:00:30 +03:00
$this -> dbh -> setAttribute ( $attribute , $value );
}
/**
* getIterator
*
2006-05-30 12:42:10 +04:00
* @ return ArrayIterator
*/
2006-12-29 17:40:47 +03:00
public function getIterator ()
{
2006-12-29 17:01:31 +03:00
if ( $this -> listener instanceof Doctrine_Db_Profiler )
2006-11-11 23:00:30 +03:00
return $this -> listener ;
2006-05-30 12:42:10 +04:00
}
/**
2006-11-11 23:00:30 +03:00
* count
2006-05-30 12:42:10 +04:00
* returns the number of executed queries
2006-11-11 23:00:30 +03:00
*
2006-05-30 12:42:10 +04:00
* @ return integer
*/
2006-12-29 17:40:47 +03:00
public function count ()
{
2006-11-11 23:00:30 +03:00
return $this -> querySequence ;
2006-12-29 17:01:31 +03:00
}
2006-05-30 12:42:10 +04:00
}