eZ Publish  [4.0]
ezdb.php
Go to the documentation of this file.
00001 <?php
00002 //
00003 // $Id$
00004 //
00005 // Definition of eZDB class
00006 //
00007 // Created on: <12-Feb-2002 15:41:03 bf>
00008 //
00009 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00010 // SOFTWARE NAME: eZ Publish
00011 // SOFTWARE RELEASE: 4.0.x
00012 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS
00013 // SOFTWARE LICENSE: GNU General Public License v2.0
00014 // NOTICE: >
00015 //   This program is free software; you can redistribute it and/or
00016 //   modify it under the terms of version 2.0  of the GNU General
00017 //   Public License as published by the Free Software Foundation.
00018 //
00019 //   This program is distributed in the hope that it will be useful,
00020 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
00021 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022 //   GNU General Public License for more details.
00023 //
00024 //   You should have received a copy of version 2.0 of the GNU General
00025 //   Public License along with this program; if not, write to the Free
00026 //   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00027 //   MA 02110-1301, USA.
00028 //
00029 //
00030 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00031 //
00032 
00033 /*! \file ezdb.php
00034  Database abstraction layer.
00035 */
00036 
00037 /*! \defgroup eZDB Database abstraction layer */
00038 
00039 /*!
00040   \class eZDB ezdb.php
00041   \ingroup eZDB
00042   \brief The eZDB class provides database wrapper functions
00043   The eZ db library procides a database independent framework for
00044   SQL databases. The current supported databases are: PostgreSQL and
00045   MySQL.
00046 
00047   eZ db is designed to be used with the following type subset of SQL:
00048   int, float, varchar and text.
00049 
00050   To store date and time values int's are used. eZ locale is used to
00051   present the date and times on a localized format. That way we don't have
00052   to worry about the different date and time formats used in the different
00053   databases.
00054 
00055   Auto incrementing numbers, sequences, are used to generate unique id's
00056   for a table row. This functionality is abstracted as it works different
00057   in the different databases.
00058 
00059   Limit and offset functionality is also abstracted by the eZ db library.
00060 
00061   eZ db is designed to use lowercase in all table/column names. This is
00062   done to prevent errors as the different databases handles this differently.
00063   Especially when returning the data as an associative array.
00064 
00065   \code
00066   // include the library
00067   //include_once( 'lib/ezdb/classes/ezdb.php' );
00068 
00069   // Get the current database instance
00070   // will create a new database object and connect to the database backend
00071   // if there is already created an instance for this session the existing
00072   // object will be returned.
00073   // the settings for the database connections are set in site.ini
00074   $db = eZDB::instance();
00075 
00076   // Run a simple query
00077   $db->query( 'DELETE FROM sql_test' );
00078 
00079   // insert some data
00080   $str = $db->escapeString( "Testing escaping'\"" );
00081   $db->query( "INSERT INTO sql_test ( name, description ) VALUES ( 'New test', '$str' )" );
00082 
00083   // Get the last serial value for the sql_test table
00084   $rowID = $db->lastSerialID( 'sql_test', 'id' );
00085 
00086   // fetch some data into an array of associative arrays
00087   $rows = $db->arrayQuery( 'SELECT * FROM sql_test' );
00088 
00089   foreach ( $rows as $row )
00090   {
00091      print( $row['name'] );
00092   }
00093 
00094   // fetch some data with a limit
00095   // will return the 10 first rows in the result
00096   $ret = $db->arrayQuery( 'SELECT id, name, description, rownum FROM sql_test',
00097                            array( 'offset' => 0, 'limit' => 10 ) );
00098 
00099   // check which implementation we're running
00100   print( $db->databaseName() );
00101 
00102   \endcode
00103 
00104   \sa eZLocale eZINI
00105 */
00106 
00107 require_once( 'lib/ezutils/classes/ezdebug.php' );
00108 
00109 class eZDB
00110 {
00111     /*!
00112       Constructor.
00113       NOTE: Should not be used.
00114     */
00115     private function __construct()
00116     {
00117         eZDebug::writeError( 'This class should not be instantiated', 'eZDB::eZDB' );
00118     }
00119 
00120     /*!
00121       \static
00122       Returns an instance of the database object.
00123     */
00124     static function hasInstance()
00125     {
00126         return isset( $GLOBALS['eZDBGlobalInstance'] ) && $GLOBALS['eZDBGlobalInstance'] instanceof eZDBInterface;
00127     }
00128 
00129     /*!
00130      \static
00131      Sets the global database instance to \a $instance.
00132     */
00133     static function setInstance( $instance )
00134     {
00135         $GLOBALS['eZDBGlobalInstance'] = $instance;
00136     }
00137 
00138     /*!
00139       \static
00140       Returns an instance of the database object.
00141       If you want to change the current database values you should set \a $forceNewInstance to \c true to force a new instance.
00142     */
00143     static function instance( $databaseImplementation = false, $databaseParameters = false, $forceNewInstance = false )
00144     {
00145         $impl =& $GLOBALS['eZDBGlobalInstance'];
00146 
00147         $fetchInstance = false;
00148         if ( !( $impl instanceof eZDBInterface ) )
00149             $fetchInstance = true;
00150 
00151         if ( $forceNewInstance  )
00152         {
00153             unset($impl);
00154             $impl = false;
00155             $fetchInstance = true;
00156         }
00157 
00158         $useDefaults = true;
00159         if ( is_array( $databaseParameters ) and isset( $databaseParameters['use_defaults'] ) )
00160             $useDefaults = $databaseParameters['use_defaults'];
00161 
00162         if ( $fetchInstance )
00163         {
00164             //include_once( 'lib/ezutils/classes/ezini.php' );
00165             $ini = eZINI::instance();
00166             if ( $databaseImplementation === false and $useDefaults )
00167                 $databaseImplementation = $ini->variable( 'DatabaseSettings', 'DatabaseImplementation' );
00168 
00169             $server = $user = $pwd = $db = $usePersistentConnection = $port = false;
00170             if ( $useDefaults )
00171                 list( $server, $port, $user, $pwd, $db, $usePersistentConnection ) =
00172                     $ini->variableMulti( 'DatabaseSettings', array( 'Server', 'Port', 'User', 'Password', 'Database', 'UsePersistentConnection', ) );
00173 
00174             $socketPath = false;
00175             if ( $useDefaults )
00176             {
00177                 $socket = $ini->variable( 'DatabaseSettings', 'Socket' );
00178                 if ( trim( $socket != "" ) and $socket != "disabled" )
00179                 {
00180                     $socketPath = $socket;
00181                 }
00182             }
00183 
00184             // Check slave servers
00185             $slaveServer = null;
00186             $slaveServerPort = null;
00187             $slaveServerUser = null;
00188             $slaveServerPassword = null;
00189             $slaveServerDatabase = null;
00190             $useSlave = $ini->variable( 'DatabaseSettings', 'UseSlaveServer' );
00191             if ( $useSlave == "enabled" )
00192             {
00193                 $slaveServers = $ini->variable( 'DatabaseSettings', 'SlaveServerArray' );
00194                 $slaveServerPorts = $ini->variable( 'DatabaseSettings', 'SlaveServerPort' );
00195                 $slaveServerUsers = $ini->variable( 'DatabaseSettings', 'SlaverServerUser' );
00196                 $slaveServerPasswords = $ini->variable( 'DatabaseSettings', 'SlaverServerPassword' );
00197                 $slaveServerDatabases = $ini->variable( 'DatabaseSettings', 'SlaverServerDatabase' );
00198                 $numberServers = count( $slaveServers );
00199                 if ( $numberServers > 1 )
00200                 {
00201                     $index = rand( 1, $numberServers ) - 1;
00202                 }
00203                 else
00204                     $index = 0;
00205                 $slaveServer = $slaveServers[$index];
00206                 $slaveServerPort = $slaveServerPorts[$index];
00207                 $slaveServerUser = $slaveServerUsers[$index];
00208                 $slaveServerPassword = $slaveServerPasswords[$index];
00209                 $slaveServerDatabase = $slaveServerDatabases[$index];
00210             }
00211 
00212             list( $charset, $retries ) =
00213                 $ini->variableMulti( 'DatabaseSettings', array( 'Charset', 'ConnectRetries' ) );
00214 
00215             $isInternalCharset = false;
00216             if ( trim( $charset ) == '' )
00217             {
00218                 $charset = eZTextCodec::internalCharset();
00219                 $isInternalCharset = true;
00220             }
00221             $builtinEncoding = ( $ini->variable( 'DatabaseSettings', 'UseBuiltinEncoding' ) == 'true' );
00222 
00223             $extraPluginPathArray = $ini->variableArray( 'DatabaseSettings', 'DatabasePluginPath' );
00224             $pluginPathArray = array_merge( array( 'lib/ezdb/classes/' ),
00225                                             $extraPluginPathArray );
00226             $impl = null;
00227 
00228             $useSlaveServer = false;
00229             if ( $useSlave == "enabled" )
00230                 $useSlaveServer = true;
00231             $defaultDatabaseParameters = array( 'server' => $server,
00232                                                 'port' => $port,
00233                                                 'user' => $user,
00234                                                 'password' => $pwd,
00235                                                 'database' => $db,
00236                                                 'use_slave_server' => $useSlaveServer,
00237                                                 'slave_server' => $slaveServer,
00238                                                 'slave_port' => $slaveServerPort,
00239                                                 'slave_user' => $slaveServerUser,
00240                                                 'slave_password' => $slaveServerPassword,
00241                                                 'slave_database' => $slaveServerDatabase,
00242                                                 'charset' => $charset,
00243                                                 'is_internal_charset' => $isInternalCharset,
00244                                                 'socket' => $socketPath,
00245                                                 'builtin_encoding' => $builtinEncoding,
00246                                                 'connect_retries' => $retries,
00247                                                 'use_persistent_connection' => $usePersistentConnection,
00248                                                 'show_errors' => true );
00249             /* This looks funny, but is needed to fix a crash in PHP */
00250             $b = $databaseParameters;
00251             $databaseParameters = $defaultDatabaseParameters;
00252             if ( isset( $b['server'] ) )
00253                 $databaseParameters['server'] = $b['server'];
00254             if ( isset( $b['port'] ) )
00255                 $databaseParameters['port'] = $b['port'];
00256             if ( isset( $b['user'] ) )
00257                 $databaseParameters['user'] = $b['user'];
00258             if ( isset( $b['password'] ) )
00259                 $databaseParameters['password'] = $b['password'];
00260             if ( isset( $b['database'] ) )
00261                 $databaseParameters['database'] = $b['database'];
00262             if ( isset( $b['use_slave_server'] ) )
00263                 $databaseParameters['use_slave_server'] = $b['use_slave_server'];
00264             if ( isset( $b['slave_server'] ) )
00265                 $databaseParameters['slave_server'] = $b['slave_server'];
00266             if ( isset( $b['slave_port'] ) )
00267                 $databaseParameters['slave_port'] = $b['slave_port'];
00268             if ( isset( $b['slave_user'] ) )
00269                 $databaseParameters['slave_user'] = $b['slave_user'];
00270             if ( isset( $b['slave_password'] ) )
00271                 $databaseParameters['slave_password'] = $b['slave_password'];
00272             if ( isset( $b['slave_database'] ) )
00273                 $databaseParameters['slave_database'] = $b['slave_database'];
00274             if ( isset( $b['charset'] ) )
00275             {
00276                 $databaseParameters['charset'] = $b['charset'];
00277                 $databaseParameters['is_internal_charset'] = false;
00278             }
00279             if ( isset( $b['socket'] ) )
00280                 $databaseParameters['socket'] = $b['socket'];
00281             if ( isset( $b['builtin_encoding'] ) )
00282                 $databaseParameters['builtin_encoding'] = $b['builtin_encoding'];
00283             if ( isset( $b['connect_retries'] ) )
00284                 $databaseParameters['connect_retries'] = $b['connect_retries'];
00285             if ( isset( $b['use_persistent_connection'] ) )
00286                 $databaseParameters['use_persistent_connection'] = $b['use_persistent_connection'];
00287             if ( isset( $b['show_errors'] ) )
00288                 $databaseParameters['show_errors'] = $b['show_errors'];
00289 
00290             // Search for the db interface implementations in active extensions directories.
00291             //include_once( 'lib/ezutils/classes/ezextension.php' );
00292             $baseDirectory         = eZExtension::baseDirectory();
00293             $extensionDirectories  = eZExtension::activeExtensions();
00294             $extensionDirectories  = array_unique( $extensionDirectories );
00295             $repositoryDirectories = array();
00296             foreach ( $extensionDirectories as $extDir )
00297             {
00298                 $newRepositoryDir = "$baseDirectory/$extDir/ezdb/dbms-drivers/";
00299                 if ( file_exists( $newRepositoryDir ) )
00300                     $repositoryDirectories[] = $newRepositoryDir;
00301             }
00302             $repositoryDirectories = array_merge( $repositoryDirectories, $pluginPathArray );
00303 
00304             foreach( $repositoryDirectories as $repositoryDir )
00305             {
00306                 // If we have an alias get the real name
00307                 $aliasList = $ini->variable( 'DatabaseSettings', 'ImplementationAlias' );
00308                 if ( isset( $aliasList[$databaseImplementation] ) )
00309                 {
00310                     $databaseImplementation = $aliasList[$databaseImplementation];
00311                 }
00312 
00313                 $dbFile = $repositoryDir . $databaseImplementation . 'db.php';
00314                 if ( file_exists( $dbFile ) )
00315                 {
00316                     include_once( $dbFile );
00317                     $className = $databaseImplementation . 'db';
00318                     $impl = new $className( $databaseParameters );
00319                     break;
00320                 }
00321             }
00322             if ( $impl === null )
00323             {
00324                 //include_once( 'lib/ezdb/classes/eznulldb.php' );
00325                 $impl = new eZNullDB( $databaseParameters );
00326                 $impl->ErrorMessage = "No database handler was found for '$databaseImplementation'";
00327                 $impl->ErrorNumber = -1;
00328                 if ( $databaseParameters['show_errors'] )
00329                 {
00330                     eZDebug::writeError( 'Database implementation not supported: ' . $databaseImplementation, 'eZDB::instance' );
00331                 }
00332             }
00333 
00334         }
00335         return $impl;
00336     }
00337 
00338     /*!
00339       Checks transaction counter
00340       If the current transaction counter is 1 or higher
00341       means 1 or more transactions are running and a negative value
00342       means something is wrong.
00343       Prints the error.
00344     */
00345     static function checkTransactionCounter()
00346     {
00347         $result = true;
00348         //include_once( 'lib/ezutils/classes/ezini.php' );
00349         $ini = eZINI::instance();
00350         $checkValidity = ( $ini->variable( "SiteAccessSettings", "CheckValidity" ) == "true" );
00351         if ( $checkValidity )
00352             return $result;
00353 
00354         $db = eZDB::instance();
00355 
00356         if ( $db->transactionCounter() > 0 )
00357         {
00358             $result = array();
00359             $result['error'] = "Internal transaction counter mismatch : " . $db->transactionCounter() . ". Should be zero.";
00360             eZDebug::writeError( $result['error'] );
00361             $stack = $db->generateFailedTransactionStack();
00362             if ( $stack !== false )
00363             {
00364                 eZDebug::writeError( $stack, 'Transaction stack' );
00365             }
00366             //include_once( 'lib/ezutils/classes/ezini.php' );
00367             $ini = eZINI::instance();
00368             // In debug mode the transaction will be invalidated causing the top-level commit
00369             // to issue an error.
00370             if ( $ini->variable( "DatabaseSettings", "DebugTransactions" ) == "enabled" )
00371             {
00372                 $db->invalidateTransaction();
00373                 $db->reportError();
00374             }
00375             else
00376             {
00377                 while ( $db->transactionCounter() > 0 )
00378                 {
00379                     $db->commit();
00380                 }
00381             }
00382         }
00383 
00384         return $result;
00385     }
00386 
00387 }
00388 
00389 ?>