|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing the eZDB class. 00004 * 00005 * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved. 00006 * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2 00007 * @version //autogentag// 00008 * @package lib 00009 */ 00010 00011 /*! \defgroup eZDB Database abstraction layer */ 00012 00013 /*! 00014 \class eZDB ezdb.php 00015 \ingroup eZDB 00016 \brief The eZDB class provides database wrapper functions 00017 The eZ db library procides a database independent framework for 00018 SQL databases. The current supported databases are: PostgreSQL and 00019 MySQL. 00020 00021 eZ db is designed to be used with the following type subset of SQL: 00022 int, float, varchar and text. 00023 00024 To store date and time values int's are used. eZ locale is used to 00025 present the date and times on a localized format. That way we don't have 00026 to worry about the different date and time formats used in the different 00027 databases. 00028 00029 Auto incrementing numbers, sequences, are used to generate unique id's 00030 for a table row. This functionality is abstracted as it works different 00031 in the different databases. 00032 00033 Limit and offset functionality is also abstracted by the eZ db library. 00034 00035 eZ db is designed to use lowercase in all table/column names. This is 00036 done to prevent errors as the different databases handles this differently. 00037 Especially when returning the data as an associative array. 00038 00039 \code 00040 00041 // Get the current database instance 00042 // will create a new database object and connect to the database backend 00043 // if there is already created an instance for this session the existing 00044 // object will be returned. 00045 // the settings for the database connections are set in site.ini 00046 $db = eZDB::instance(); 00047 00048 // Run a simple query 00049 $db->query( 'DELETE FROM sql_test' ); 00050 00051 // insert some data 00052 $str = $db->escapeString( "Testing escaping'\"" ); 00053 $db->query( "INSERT INTO sql_test ( name, description ) VALUES ( 'New test', '$str' )" ); 00054 00055 // Get the last serial value for the sql_test table 00056 $rowID = $db->lastSerialID( 'sql_test', 'id' ); 00057 00058 // fetch some data into an array of associative arrays 00059 $rows = $db->arrayQuery( 'SELECT * FROM sql_test' ); 00060 00061 foreach ( $rows as $row ) 00062 { 00063 print( $row['name'] ); 00064 } 00065 00066 // fetch some data with a limit 00067 // will return the 10 first rows in the result 00068 $ret = $db->arrayQuery( 'SELECT id, name, description, rownum FROM sql_test', 00069 array( 'offset' => 0, 'limit' => 10 ) ); 00070 00071 // check which implementation we're running 00072 print( $db->databaseName() ); 00073 00074 \endcode 00075 00076 \sa eZLocale eZINI 00077 */ 00078 00079 class eZDB 00080 { 00081 /*! 00082 Constructor. 00083 NOTE: Should not be used. 00084 */ 00085 private function __construct() 00086 { 00087 eZDebug::writeError( 'This class should not be instantiated', __METHOD__ ); 00088 } 00089 00090 /*! 00091 \static 00092 Returns an instance of the database object. 00093 */ 00094 static function hasInstance() 00095 { 00096 return isset( $GLOBALS['eZDBGlobalInstance'] ) && $GLOBALS['eZDBGlobalInstance'] instanceof eZDBInterface; 00097 } 00098 00099 /*! 00100 \static 00101 Sets the global database instance to \a $instance. 00102 */ 00103 static function setInstance( $instance ) 00104 { 00105 $GLOBALS['eZDBGlobalInstance'] = $instance; 00106 } 00107 00108 /** 00109 * Returns a shared instance of the eZDBInterface class aka database object. 00110 * If you want to change the current database values you should use $forceNewInstance. 00111 * 00112 * @param string|false $databaseImplementation 00113 * @param array|false $databaseParameters If array, then key 'use_defaults' (bool) is used. 00114 * @param bool $forceNewInstance 00115 * @return eZDBInterface 00116 */ 00117 static function instance( $databaseImplementation = false, $databaseParameters = false, $forceNewInstance = false ) 00118 { 00119 $impl =& $GLOBALS['eZDBGlobalInstance']; 00120 00121 $fetchInstance = false; 00122 if ( !( $impl instanceof eZDBInterface ) ) 00123 $fetchInstance = true; 00124 00125 if ( $forceNewInstance ) 00126 { 00127 unset($impl); 00128 $impl = false; 00129 $fetchInstance = true; 00130 } 00131 00132 $useDefaults = true; 00133 if ( is_array( $databaseParameters ) and isset( $databaseParameters['use_defaults'] ) ) 00134 $useDefaults = $databaseParameters['use_defaults']; 00135 00136 if ( $fetchInstance ) 00137 { 00138 $ini = eZINI::instance(); 00139 if ( $databaseImplementation === false and $useDefaults ) 00140 $databaseImplementation = $ini->variable( 'DatabaseSettings', 'DatabaseImplementation' ); 00141 00142 $server = $user = $pwd = $db = $usePersistentConnection = $port = false; 00143 if ( $useDefaults ) 00144 list( $server, $port, $user, $pwd, $db, $usePersistentConnection ) = 00145 $ini->variableMulti( 'DatabaseSettings', array( 'Server', 'Port', 'User', 'Password', 'Database', 'UsePersistentConnection', ) ); 00146 00147 $socketPath = false; 00148 if ( $useDefaults ) 00149 { 00150 $socket = $ini->variable( 'DatabaseSettings', 'Socket' ); 00151 if ( trim( $socket != "" ) and $socket != "disabled" ) 00152 { 00153 $socketPath = $socket; 00154 } 00155 } 00156 00157 // Check slave servers 00158 $slaveServer = null; 00159 $slaveServerPort = null; 00160 $slaveServerUser = null; 00161 $slaveServerPassword = null; 00162 $slaveServerDatabase = null; 00163 $useSlave = $ini->variable( 'DatabaseSettings', 'UseSlaveServer' ); 00164 if ( $useSlave == "enabled" ) 00165 { 00166 $slaveServers = $ini->variable( 'DatabaseSettings', 'SlaveServerArray' ); 00167 $slaveServerPorts = $ini->variable( 'DatabaseSettings', 'SlaveServerPort' ); 00168 $slaveServerUsers = $ini->variable( 'DatabaseSettings', 'SlaverServerUser' ); 00169 $slaveServerPasswords = $ini->variable( 'DatabaseSettings', 'SlaverServerPassword' ); 00170 $slaveServerDatabases = $ini->variable( 'DatabaseSettings', 'SlaverServerDatabase' ); 00171 $numberServers = count( $slaveServers ); 00172 if ( $numberServers > 1 ) 00173 { 00174 $index = mt_rand( 1, $numberServers ) - 1; 00175 } 00176 else 00177 $index = 0; 00178 $slaveServer = $slaveServers[$index]; 00179 $slaveServerPort = $slaveServerPorts[$index]; 00180 $slaveServerUser = $slaveServerUsers[$index]; 00181 $slaveServerPassword = $slaveServerPasswords[$index]; 00182 $slaveServerDatabase = $slaveServerDatabases[$index]; 00183 } 00184 00185 list( $charset, $retries ) = 00186 $ini->variableMulti( 'DatabaseSettings', array( 'Charset', 'ConnectRetries' ) ); 00187 00188 $isInternalCharset = false; 00189 if ( trim( $charset ) == '' ) 00190 { 00191 $charset = eZTextCodec::internalCharset(); 00192 $isInternalCharset = true; 00193 } 00194 $builtinEncoding = ( $ini->variable( 'DatabaseSettings', 'UseBuiltinEncoding' ) == 'true' ); 00195 00196 $impl = null; 00197 00198 $useSlaveServer = false; 00199 if ( $useSlave == "enabled" ) 00200 $useSlaveServer = true; 00201 $defaultDatabaseParameters = array( 'server' => $server, 00202 'port' => $port, 00203 'user' => $user, 00204 'password' => $pwd, 00205 'database' => $db, 00206 'use_slave_server' => $useSlaveServer, 00207 'slave_server' => $slaveServer, 00208 'slave_port' => $slaveServerPort, 00209 'slave_user' => $slaveServerUser, 00210 'slave_password' => $slaveServerPassword, 00211 'slave_database' => $slaveServerDatabase, 00212 'charset' => $charset, 00213 'is_internal_charset' => $isInternalCharset, 00214 'socket' => $socketPath, 00215 'builtin_encoding' => $builtinEncoding, 00216 'connect_retries' => $retries, 00217 'use_persistent_connection' => $usePersistentConnection, 00218 'show_errors' => true ); 00219 /* This looks funny, but is needed to fix a crash in PHP */ 00220 $b = $databaseParameters; 00221 $databaseParameters = $defaultDatabaseParameters; 00222 if ( isset( $b['server'] ) ) 00223 $databaseParameters['server'] = $b['server']; 00224 if ( isset( $b['port'] ) ) 00225 $databaseParameters['port'] = $b['port']; 00226 if ( isset( $b['user'] ) ) 00227 $databaseParameters['user'] = $b['user']; 00228 if ( isset( $b['password'] ) ) 00229 $databaseParameters['password'] = $b['password']; 00230 if ( isset( $b['database'] ) ) 00231 $databaseParameters['database'] = $b['database']; 00232 if ( isset( $b['use_slave_server'] ) ) 00233 $databaseParameters['use_slave_server'] = $b['use_slave_server']; 00234 if ( isset( $b['slave_server'] ) ) 00235 $databaseParameters['slave_server'] = $b['slave_server']; 00236 if ( isset( $b['slave_port'] ) ) 00237 $databaseParameters['slave_port'] = $b['slave_port']; 00238 if ( isset( $b['slave_user'] ) ) 00239 $databaseParameters['slave_user'] = $b['slave_user']; 00240 if ( isset( $b['slave_password'] ) ) 00241 $databaseParameters['slave_password'] = $b['slave_password']; 00242 if ( isset( $b['slave_database'] ) ) 00243 $databaseParameters['slave_database'] = $b['slave_database']; 00244 if ( isset( $b['charset'] ) ) 00245 { 00246 $databaseParameters['charset'] = $b['charset']; 00247 $databaseParameters['is_internal_charset'] = false; 00248 } 00249 if ( isset( $b['socket'] ) ) 00250 $databaseParameters['socket'] = $b['socket']; 00251 if ( isset( $b['builtin_encoding'] ) ) 00252 $databaseParameters['builtin_encoding'] = $b['builtin_encoding']; 00253 if ( isset( $b['connect_retries'] ) ) 00254 $databaseParameters['connect_retries'] = $b['connect_retries']; 00255 if ( isset( $b['use_persistent_connection'] ) ) 00256 $databaseParameters['use_persistent_connection'] = $b['use_persistent_connection']; 00257 if ( isset( $b['show_errors'] ) ) 00258 $databaseParameters['show_errors'] = $b['show_errors']; 00259 00260 $optionArray = array( 'iniFile' => 'site.ini', 00261 'iniSection' => 'DatabaseSettings', 00262 'iniVariable' => 'ImplementationAlias', 00263 'handlerIndex' => $databaseImplementation, 00264 'handlerParams' => array( $databaseParameters ) ); 00265 00266 $options = new ezpExtensionOptions( $optionArray ); 00267 00268 $impl = eZExtension::getHandlerClass( $options ); 00269 00270 if ( !$impl ) 00271 { 00272 $impl = new eZNullDB( $databaseParameters ); 00273 $impl->ErrorMessage = "No database handler was found for '$databaseImplementation'"; 00274 $impl->ErrorNumber = -1; 00275 if ( $databaseParameters['show_errors'] ) 00276 { 00277 eZDebug::writeError( 'Database implementation not supported: ' . $databaseImplementation, __METHOD__ ); 00278 } 00279 } 00280 00281 $impl->setErrorHandling( self::$errorHandling ); 00282 } 00283 return $impl; 00284 } 00285 00286 /*! 00287 Checks transaction counter 00288 If the current transaction counter is 1 or higher 00289 means 1 or more transactions are running and a negative value 00290 means something is wrong. 00291 Prints the error. 00292 */ 00293 static function checkTransactionCounter() 00294 { 00295 $result = true; 00296 $ini = eZINI::instance(); 00297 $checkValidity = ( $ini->variable( "SiteAccessSettings", "CheckValidity" ) == "true" ); 00298 if ( $checkValidity ) 00299 return $result; 00300 00301 $db = eZDB::instance(); 00302 00303 if ( $db->transactionCounter() > 0 ) 00304 { 00305 $result = array(); 00306 $result['error'] = "Internal transaction counter mismatch : " . $db->transactionCounter() . ". Should be zero."; 00307 eZDebug::writeError( $result['error'] ); 00308 $stack = $db->generateFailedTransactionStack(); 00309 if ( $stack !== false ) 00310 { 00311 eZDebug::writeError( $stack, 'Transaction stack' ); 00312 } 00313 $ini = eZINI::instance(); 00314 // In debug mode the transaction will be invalidated causing the top-level commit 00315 // to issue an error. 00316 if ( $ini->variable( "DatabaseSettings", "DebugTransactions" ) == "enabled" ) 00317 { 00318 $db->invalidateTransaction(); 00319 $db->reportError(); 00320 } 00321 else 00322 { 00323 while ( $db->transactionCounter() > 0 ) 00324 { 00325 $db->commit(); 00326 } 00327 } 00328 } 00329 00330 return $result; 00331 } 00332 00333 /** 00334 * Sets the default eZDB error handling mode. 00335 * Use eZDB::instance()->setErrorHandling() with the same parameters to set error handling for one instance only 00336 * 00337 * @param int $errorHandling 00338 * Possible values are:pm 00339 * - eZDB::ERROR_HANDLING_STANDARD: backward compatible error handling, using reportError 00340 * - eZDB::ERROR_HANDLING_EXCEPTION: using exceptions 00341 * @throw RuntimeException thrown when an invalid error handling is given 00342 * @access private 00343 * @since 4.5 00344 */ 00345 static function setErrorHandling( $errorHandling ) 00346 { 00347 if ( $errorHandling != self::ERROR_HANDLING_EXCEPTIONS && $errorHandling != self::ERROR_HANDLING_STANDARD ) 00348 throw new RuntimeException( "Unknown eZDB error handling mode '$errorHandling'" ); 00349 self::$errorHandling = $errorHandling; 00350 00351 if ( self::hasInstance() ) 00352 { 00353 self::instance()->setErrorHandling( $errorHandling ); 00354 } 00355 } 00356 00357 /** 00358 * Error handling mode 00359 */ 00360 const ERROR_HANDLING_STANDARD = 1; 00361 const ERROR_HANDLING_EXCEPTIONS = 2; 00362 00363 protected static $errorHandling = self::ERROR_HANDLING_STANDARD; 00364 } 00365 00366 ?>