|
eZ Publish
[4.0]
|
00001 <?php 00002 // 00003 // Definition of eZDBFileHandler class 00004 // 00005 // Created on: <19-Apr-2006 16:01:30 vs> 00006 // 00007 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00008 // SOFTWARE NAME: eZ Publish 00009 // SOFTWARE RELEASE: 4.0.x 00010 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS 00011 // SOFTWARE LICENSE: GNU General Public License v2.0 00012 // NOTICE: > 00013 // This program is free software; you can redistribute it and/or 00014 // modify it under the terms of version 2.0 of the GNU General 00015 // Public License as published by the Free Software Foundation. 00016 // 00017 // This program is distributed in the hope that it will be useful, 00018 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 // GNU General Public License for more details. 00021 // 00022 // You should have received a copy of version 2.0 of the GNU General 00023 // Public License along with this program; if not, write to the Free 00024 // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 00025 // MA 02110-1301, USA. 00026 // 00027 // 00028 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00029 // 00030 00031 /*! \file ezdbfilehandler.php 00032 00033 Note: Not all code is using this class for cluster access, see index_image_mysql.php and index_image_pgsql.php for more custom code. 00034 */ 00035 00036 require_once( 'lib/ezutils/classes/ezdebugsetting.php' ); 00037 require_once( 'lib/ezutils/classes/ezdebug.php' ); 00038 00039 class eZDBFileHandler 00040 { 00041 /*! 00042 Controls whether file data from database is cached on the local filesystem. 00043 \note This is primarily available for debugging purposes. 00044 */ 00045 const LOCAL_CACHE = 1; 00046 00047 /*! 00048 Controls the maximum number of metdata entries to keep in memory for this request. 00049 If the limit is reached the least used entries are removed. 00050 */ 00051 const INFOCACHE_MAX = 200; 00052 00053 /** 00054 * Constructor. 00055 * 00056 * $filePath File path. If specified, file metadata is fetched in the constructor. 00057 */ 00058 function eZDBFileHandler( $filePath = false ) 00059 { 00060 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00061 eZDebugSetting::writeDebug( 'kernel-clustering', "db::ctor( '$filePath' )" ); 00062 00063 // Init backend. 00064 if ( !isset( $GLOBALS['eZDBFileHandler_chosen_backend_class'] ) ) 00065 { 00066 require_once( 'lib/ezutils/classes/ezini.php' ); 00067 $fileINI = eZINI::instance( 'file.ini' ); 00068 $backendName = 'mysql'; 00069 if ( $fileINI->hasVariable( 'ClusteringSettings', 'DBBackend' ) ) 00070 $backendName = $fileINI->variable( 'ClusteringSettings', 'DBBackend' ); 00071 00072 require_once( 'kernel/classes/ezclusterfilehandler.php' ); 00073 $searchPathArray = eZClusterFileHandler::searchPathArray(); 00074 00075 foreach ( $searchPathArray as $searchPath ) 00076 { 00077 $includeFileName = "$searchPath/dbbackends/$backendName.php"; 00078 if ( is_readable( $includeFileName ) ) 00079 { 00080 include_once( $includeFileName ); 00081 $backendClassName = "eZDBFileHandler${backendName}Backend"; 00082 $GLOBALS['eZDBFileHandler_chosen_backend_class'] = $backendClassName; 00083 } 00084 } 00085 00086 if ( !isset( $GLOBALS['eZDBFileHandler_chosen_backend_class'] ) ) 00087 { 00088 eZDebug::writeError( "Cannot find ezdb cluster file backend: '$backendName'" ); 00089 return; 00090 } 00091 } 00092 00093 $backendClassName = $GLOBALS['eZDBFileHandler_chosen_backend_class']; 00094 $this->backend = new $backendClassName; 00095 $this->backend->_connect( false ); 00096 $this->backendVerify = null; 00097 $this->filePath = $filePath; 00098 $this->metaData = false; 00099 // $this->metaData['name'] = $filePath; 00100 00101 // $this->loadMetaData(); 00102 } 00103 00104 /*! 00105 \public 00106 Load file meta information. 00107 00108 \param $force File stats will be refreshed if true 00109 */ 00110 function loadMetaData( $force = false ) 00111 { 00112 // Fetch metadata. 00113 if ( $this->filePath === false ) 00114 return; 00115 00116 if ( $force && isset( $GLOBALS['eZClusterInfo'][$this->filePath] ) ) 00117 unset( $GLOBALS['eZClusterInfo'][$this->filePath] ); 00118 00119 // Checks for metadata stored in memory, useful for repeated access 00120 // to the same file in one request 00121 // TODO: On PHP5 turn into static member 00122 if ( isset( $GLOBALS['eZClusterInfo'][$this->filePath] ) ) 00123 { 00124 $GLOBALS['eZClusterInfo'][$this->filePath]['cnt'] += 1; 00125 $this->metaData = $GLOBALS['eZClusterInfo'][$this->filePath]['data']; 00126 return; 00127 } 00128 00129 $metaData = $this->backend->_fetchMetadata( $this->filePath ); 00130 if ( $metaData ) 00131 $this->metaData = $metaData; 00132 else 00133 $this->metaData = false; 00134 00135 // Clean up old entries if the maximum count is reached 00136 if ( isset( $GLOBALS['eZClusterInfo'] ) && 00137 count( $GLOBALS['eZClusterInfo'] ) >= self::INFOCACHE_MAX ) 00138 { 00139 usort( $GLOBALS['eZClusterInfo'], 00140 create_function( '$a, $b', 00141 '$a=$a["cnt"]; $b=$b["cnt"]; if ( $a > $b ) return -1; else if ( $a == $b ) return 0; else return 1;' ) ); 00142 array_pop( $GLOBALS['eZClusterInfo'] ); 00143 } 00144 $GLOBALS['eZClusterInfo'][$this->filePath] = array( 'cnt' => 1, 00145 'data' => $metaData ); 00146 } 00147 00148 /** 00149 * \public 00150 * \static 00151 * \param $filePath Path to the file being stored. 00152 * \param $scope Means something like "file category". May be used to clean caches of a certain type. 00153 * \param $delete true if the file should be deleted after storing. 00154 */ 00155 function fileStore( $filePath, $scope = false, $delete = false, $datatype = false ) 00156 { 00157 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00158 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileStore( '$filePath' )" ); 00159 00160 if ( $scope === false ) 00161 $scope = 'UNKNOWN_SCOPE'; 00162 00163 if ( $datatype === false ) 00164 $datatype = 'misc'; 00165 00166 $this->backend->_store( $filePath, $datatype, $scope ); 00167 00168 if ( $delete ) 00169 @unlink( $filePath ); 00170 } 00171 00172 /** 00173 * Store file contents. 00174 * 00175 * \public 00176 * \static 00177 */ 00178 function fileStoreContents( $filePath, $contents, $scope = false, $datatype = false ) 00179 { 00180 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00181 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileStoreContents( '$filePath' )" ); 00182 00183 if ( $scope === false ) 00184 $scope = 'UNKNOWN_SCOPE'; 00185 00186 if ( $datatype === false ) 00187 $datatype = 'misc'; 00188 00189 $this->backend->_storeContents( $filePath, $contents, $scope, $datatype ); 00190 } 00191 00192 /** 00193 * Store file contents. 00194 * 00195 * \public 00196 * 00197 * \param $storeLocally If true the file will also be stored on the local file system. 00198 */ 00199 function storeContents( $contents, $scope = false, $datatype = false, $storeLocally = false ) 00200 { 00201 if ( $scope === false ) 00202 $scope = 'UNKNOWN_SCOPE'; 00203 00204 if ( $datatype === false ) 00205 $datatype = 'misc'; 00206 00207 $filePath = $this->filePath; 00208 eZDebugSetting::writeDebug( 'kernel-clustering', "db::storeContents( '$filePath' )" ); 00209 $this->backend->_storeContents( $filePath, $contents, $scope, $datatype ); 00210 if ( $storeLocally ) 00211 { 00212 //include_once( 'lib/ezfile/classes/ezfile.php' ); 00213 eZFile::create( basename( $filePath ), dirname( $filePath ), $contents, true ); 00214 } 00215 } 00216 00217 /** 00218 * Fetches file from db and saves it in FS under the same name. 00219 * 00220 * \public 00221 * \static 00222 */ 00223 function fileFetch( $filePath ) 00224 { 00225 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00226 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileFetch( '$filePath' )" ); 00227 00228 return $this->backend->_fetch( $filePath ); 00229 } 00230 00231 /*! 00232 Creates a single transaction out of the typical file operations for accessing caches. 00233 Caches are normally ready from the database or local file, if the entry does not exist 00234 or is expired then it generates the new cache data and stores it. 00235 This method takes care of these operations and handles the custom code by performing 00236 callbacks when needed. 00237 00238 The $retrieveCallback is used when the file contents can be used (ie. not re-generation) and 00239 is called when the file is ready locally. 00240 The function will be called with the file path as the first parameter, the mtime as the second 00241 and optionally $extraData as the third. 00242 The function must return the file contents or an instance of eZClusterFileFailure which can 00243 be used to tell the system that the retrieve data cannot be used after all. 00244 $retrieveCallback can be set to null which makes the system go directly to the generation. 00245 00246 The $generateCallback is used when the file content is expired or does not exist, in this 00247 case the content must be re-generated and stored. 00248 The function will be called with the file path as the first parameter and optionally $extraData 00249 as the second. 00250 The function must return an array with information on the contents, the array consists of: 00251 - scope - The current scope of the file, is optional. 00252 - datatype - The current datatype of the file, is optional. 00253 - content - The file content, this can be any type except null. 00254 - binarydata - The binary data which is written to the file. 00255 - store - Whether *content* or *binarydata* should be stored to the file, if false it will simply return the data. Optional, by default it is true. 00256 Note: Set $generateCallback to false to disable generation callback. 00257 Note: Set $generateCallback to null to tell the function to perform a write lock but not do any generation, the generation must done be done by the caller by calling storeCache(). 00258 00259 Either *content* or *binarydata* must be supplied, if not an error is issued and it returns null. 00260 If *content* is set it will be used as the return value of this function, if not it will return the binary data. 00261 If *binarydata* is set it will be used as the binary data for the file, if not it will perform a var_export on *content* and use that as the binary data. 00262 00263 For convenience the $generateCallback function can return a string which will be considered as the 00264 binary data for the file and returned as the content. 00265 00266 For controlling how long a cache entry can be used the parameters $expiry and $ttl is used. 00267 $expiry can be set to a timestamp which controls the absolute max time for the cache, after this 00268 time/date the cache will never be used. If the value is set to a negative value or null there the 00269 expiration check is disabled. 00270 00271 $ttl (time to live) tells how many seconds the cache can live from the time it was stored. If the 00272 value is set to negative or null there is no limit for the lifetime of the cache. A value of 0 means 00273 that the cache will always expire and practically disables caching. 00274 For the cache to be used both the $expiry and $ttl check must hold. 00275 */ 00276 function processCache( $retrieveCallback, $generateCallback = null, $ttl = null, $expiry = null, $extraData = null ) 00277 { 00278 //include_once( 'kernel/classes/ezclusterfilefailure.php' ); 00279 $forceDB = false; 00280 $fname = $this->filePath; 00281 $timestamp = null; 00282 $curtime = time(); 00283 $tries = 0; 00284 00285 if ( $expiry < 0 ) 00286 $expiry = null; 00287 if ( $ttl < 0 ) 00288 $ttl = null; 00289 00290 // Main loop 00291 while ( true ) 00292 { 00293 // Start read checks 00294 // Note: The while loop is used to make it easier to break out of the "read" code 00295 while ( true ) 00296 { 00297 // No retrieve method so go directly to generate+store 00298 if ( $retrieveCallback === null || !$fname ) 00299 break; 00300 00301 if ( !self::LOCAL_CACHE ) 00302 { 00303 $forceDB = true; 00304 } 00305 else 00306 { 00307 $mtime = @filemtime( $fname ); 00308 00309 if ( eZDBFileHandler::isExpired( $fname, $mtime, $expiry, $curtime, $ttl ) ) 00310 { 00311 // Local file is older than global timestamp, check with DB 00312 eZDebugSetting::writeDebug( 'kernel-clustering', "Local file (mtime=$mtime) is older than timestamp ($expiry) and ttl($ttl), check with DB" ); 00313 $forceDB = true; 00314 } 00315 } 00316 00317 if ( !$forceDB ) 00318 { 00319 if ( $this->metaData === false ) 00320 $this->loadMetaData(); 00321 00322 if ( $this->metaData === false || $this->metaData['mtime'] < 0 ) 00323 { 00324 if ( $generateCallback !== false ) 00325 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file is deleted, need to regenerate data" ); 00326 else 00327 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file is deleted, cannot get data" ); 00328 break; 00329 } 00330 00331 if ( $this->metaData === false || eZDBFileHandler::isExpired( $fname, $mtime, $this->metaData['mtime'], $curtime, $ttl ) ) 00332 // if ( $this->metaData === false || eZDBFileHandler::isExpired( $fname, $mtime, $expiry, $curtime, $ttl ) ) 00333 { 00334 eZDebugSetting::writeDebug( 'kernel-clustering', "Local file (mtime=$mtime) is older than DB, checking with DB" ); 00335 $forceDB = true; 00336 } 00337 else 00338 { 00339 eZDebugSetting::writeDebug( 'kernel-clustering', "Processing local file $fname" ); 00340 $args = array( $fname, $mtime ); 00341 if ( $extraData !== null ) 00342 $args[] = $extraData; 00343 $retval = call_user_func_array( $retrieveCallback, $args ); 00344 if ( $retval instanceof eZClusterFileFailure ) 00345 { 00346 break; 00347 } 00348 return $retval; 00349 } 00350 } 00351 00352 if ( $this->metaData === false ) 00353 $this->loadMetaData(); 00354 00355 if ( $this->metaData === false || $this->metaData['mtime'] < 0 ) 00356 { 00357 if ( $generateCallback !== false ) 00358 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file is deleted, need to regenerate data" ); 00359 else 00360 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file is deleted, cannot get data" ); 00361 break; 00362 } 00363 00364 if ( $this->metaData !== false && !eZDBFileHandler::isExpired( $fname, $this->metaData['mtime'], $expiry, $curtime, $ttl ) ) 00365 { 00366 eZDebugSetting::writeDebug( 'kernel-clustering', "Callback from DB file $fname" ); 00367 if ( self::LOCAL_CACHE ) 00368 { 00369 $this->fetch(); 00370 00371 // Figure out which mtime to use for new file, must be larger than 00372 // mtime in DB at least. 00373 $mtime = $this->metaData['mtime'] + 1; 00374 $localmtime = @filemtime( $fname ); 00375 $mtime = max( $mtime, $localmtime ); 00376 touch( $fname, $mtime, $mtime ); 00377 clearstatcache(); // Needed because of touch() call 00378 00379 $args = array( $fname, $mtime ); 00380 if ( $extraData !== null ) 00381 $args[] = $extraData; 00382 $retval = call_user_func_array( $retrieveCallback, $args ); 00383 if ( $retval instanceof eZClusterFileFailure ) 00384 { 00385 break; 00386 } 00387 return $retval; 00388 } 00389 else 00390 { 00391 $uniquePath = $this->fetchUnique(); 00392 00393 $args = array( $fname, $this->metaData['mtime'] ); 00394 if ( $extraData !== null ) 00395 $args[] = $extraData; 00396 $retval = call_user_func_array( $retrieveCallback, $args ); 00397 $this->fileDeleteLocal( $uniquePath ); 00398 if ( $retval instanceof eZClusterFileFailure ) 00399 break; 00400 return $retval; 00401 } 00402 } 00403 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file does not exist, need to regenerate data" ); 00404 break; 00405 } 00406 00407 if ( $tries >= 2 ) 00408 { 00409 eZDebugSetting::writeDebug( 'kernel-clustering', "Reading was retried $tries times and reached the maximum, returning null" ); 00410 return null; 00411 } 00412 00413 // Generation part starts here 00414 if ( isset( $retval ) && 00415 $retval instanceof eZClusterFileFailure ) 00416 { 00417 if ( $retval->errno() != 1 ) // check for non-expiry error codes 00418 { 00419 eZDebug::writeError( "Failed to retrieve data from callback", 'eZDBFileHandler::processCache' ); 00420 return null; 00421 } 00422 $message = $retval->message(); 00423 if ( strlen( $message ) > 0 ) 00424 { 00425 eZDebugSetting::writeDebug( 'kernel-clustering', $retval->message(), "eZClusterFileFailure::processCache" ); 00426 } 00427 // the retrieved data was expired so we need to generate it, let's continue 00428 } 00429 00430 // We need to lock if we have a generate-callback or 00431 // the generation is deferred to the caller. 00432 // Note: false means no generation 00433 if ( $generateCallback !== false && $fname ) 00434 { 00435 // Lock the entry for exclusive access, if the entry does not exist 00436 // it will be inserted with mtime=-1 00437 $res = $this->backend->_exclusiveLock( $fname, 'processCache' ); 00438 if ( !$res || $res instanceof eZMySQLBackendError ) 00439 { 00440 // Cannot get exclusive lock, so return null. 00441 return null; 00442 } 00443 00444 // some db backends have advisory locking and we use a kind of 00445 // two-phase locking mechanism: 00446 // lock is first created, then we check if it was acquired or 00447 // if some other process has written the file in the meantime 00448 if ( !$this->backend->_verifyExclusiveLock( $fname, $expiry, $curtime, $ttl, 'processCache' ) ) 00449 { 00450 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file was generated while we were locked, using that instead" ); 00451 $this->backend->_rollback( 'processCache' ); // release lock 00452 $this->metaData = false; 00453 unset( $GLOBALS['eZClusterInfo'][$fname] ); 00454 ++$tries; 00455 continue; // retry reading file 00456 } 00457 } 00458 00459 // File in DB is outdated or non-existing, call write-callback to generate content 00460 if ( $generateCallback ) 00461 { 00462 $args = array( $fname ); 00463 if ( $extraData !== null ) 00464 $args[] = $extraData; 00465 $fileData = call_user_func_array( $generateCallback, $args ); 00466 return $this->storeCache( $fileData ); 00467 } 00468 00469 break; 00470 } 00471 00472 return new eZClusterFileFailure( 2, "Manual generation of file data is required, calling storeCache is required" ); 00473 } 00474 00475 /*! 00476 \static 00477 \private 00478 Calculates if the file data is expired or not. 00479 00480 \param $fname Name of file, available for easy debugging. 00481 \param $mtime Modification time of file, can be set to false if file does not exist. 00482 \param $expiry Time when file is to be expired, a value of -1 will disable this check. 00483 \param $curtime The current time to check against. 00484 \param $ttl Number of seconds the data can live, set to null to disable TTL. 00485 */ 00486 static function isExpired( $fname, $mtime, $expiry, $curtime, $ttl ) 00487 { 00488 if ( $mtime == false ) 00489 { 00490 return true; 00491 } 00492 else if ( $ttl === null ) 00493 { 00494 $ret = $mtime < $expiry; 00495 return $ret; 00496 } 00497 else 00498 { 00499 $ret = $mtime < max( $expiry, $curtime - $ttl ); 00500 return $ret; 00501 } 00502 } 00503 00504 /*! 00505 \private 00506 Stores the data in $fileData to the remote and local file and commits the transaction. 00507 00508 The parameter $fileData must contain the same as information as the $generateCallback returns as explained in processCache(). 00509 00510 \note This method is just a continuation of the code in processCache() and is not meant to be called alone since it relies on specific state in the database. 00511 */ 00512 function storeCache( $fileData ) 00513 { 00514 $fname = $this->filePath; 00515 00516 $scope = false; 00517 $datatype = false; 00518 $binaryData = null; 00519 $fileContent = null; 00520 $store = true; 00521 if ( is_array( $fileData ) ) 00522 { 00523 if ( isset( $fileData['scope'] ) ) 00524 $scope = $fileData['scope']; 00525 if ( isset( $fileData['datatype'] ) ) 00526 $datatype = $fileData['datatype']; 00527 if ( isset( $fileData['content'] ) ) 00528 $fileContent = $fileData['content']; 00529 if ( isset( $fileData['binarydata'] ) ) 00530 $binaryData = $fileData['binarydata']; 00531 if ( isset( $fileData['store'] ) ) 00532 $store = $fileData['store']; 00533 } 00534 else 00535 $binaryData = $fileData; 00536 00537 $mtime = false; 00538 $result = null; 00539 if ( $binaryData === null && 00540 $fileContent === null ) 00541 { 00542 eZDebug::writeError( "Write callback need to set the 'content' or 'binarydata' entry" ); 00543 $this->backend->_rollback( 'storeCache' ); 00544 return null; 00545 } 00546 00547 if ( $binaryData === null ) 00548 $binaryData = "<" . "?php\n\treturn ". var_export( $fileContent, true ) . ";\n?" . ">\n"; 00549 00550 if ( $fileContent === null ) 00551 $result = $binaryData; 00552 else 00553 $result = $fileContent; 00554 00555 if ( !$fname ) 00556 return $result; 00557 00558 // Check if we are allowed to store the data, if not just return the result 00559 if ( !$store ) 00560 { 00561 $this->backend->_rollback( 'storeCache' ); 00562 return $result; 00563 } 00564 00565 if ( self::LOCAL_CACHE ) 00566 { 00567 // Store content also locally 00568 eZDebugSetting::writeDebug( 'kernel-clustering', "Writing new file content to local file $fname" ); 00569 //include_once( 'lib/ezfile/classes/ezfile.php' ); 00570 eZFile::create( basename( $fname ), dirname( $fname ), $binaryData, true ); 00571 $mtime = @filemtime( $fname ); 00572 } 00573 00574 eZDebugSetting::writeDebug( 'kernel-clustering', "Writing new file content to database for $fname" ); 00575 $this->storeContents( $binaryData, $scope, $datatype, $mtime ); 00576 00577 $this->backend->_freeExclusiveLock( 'storeCache' ); 00578 00579 return $result; 00580 } 00581 00582 /*! 00583 Provides access to the file contents by downloading the file locally and 00584 calling $callback with the local filename. The callback can then process the 00585 contents and return the data in the same way as in processCache(). 00586 Downloading is only done once so the local copy is kept, while updates to the 00587 remote DB entry is synced with the local one. 00588 00589 The parameters $expiry and $extraData is the same as for processCache(). 00590 00591 \note Unlike processCache() this returns null if the file cannot be accessed. 00592 */ 00593 function processFile( $callback, $expiry = false, $extraData = null ) 00594 { 00595 $result = $this->processCache( $callback, false, null, $expiry, $extraData ); 00596 if ( $result instanceof eZClusterFileFailure ) 00597 { 00598 return null; 00599 } 00600 return $result; 00601 } 00602 00603 /** 00604 * Fetches file from db and saves it in FS under unique name. 00605 * 00606 * \public 00607 * \static 00608 * \return filename with path of a saved file. You can use this filename to get contents of file from filesystem. 00609 */ 00610 function fetchUnique( ) 00611 { 00612 $filePath = $this->filePath; 00613 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fetchUnique( '$filePath' )" ); 00614 00615 $fetchedFilePath = $this->backend->_fetch( $filePath, true ); 00616 $this->uniqueName = $fetchedFilePath; 00617 return $fetchedFilePath; 00618 } 00619 00620 /** 00621 * Fetches file from db and saves it in FS under the same name. 00622 * 00623 * \public 00624 */ 00625 function fetch( $cacheLocally = false ) 00626 { 00627 $filePath = $this->filePath; 00628 $metaData = $this->backend->_fetchMetadata( $filePath ); 00629 $mtime = @filemtime( $filePath ); 00630 if ( !$cacheLocally || 00631 $metaData === false || 00632 $mtime === false || 00633 $mtime < $metaData['mtime'] || 00634 @filesize( $filePath ) != $metaData['size'] || 00635 !is_readable( $filePath ) ) 00636 { 00637 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fetch( '$filePath' )" ); 00638 $this->backend->_fetch( $filePath ); 00639 } 00640 } 00641 00642 /** 00643 * Returns file contents. 00644 * 00645 * \public 00646 * \static 00647 * \return contents string, or false in case of an error. 00648 */ 00649 function fileFetchContents( $filePath ) 00650 { 00651 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00652 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileFetchContents( '$filePath' )" ); 00653 00654 $contents = $this->backend->_fetchContents( $filePath ); 00655 return $contents; 00656 } 00657 00658 /** 00659 * Returns file contents. 00660 * 00661 * \public 00662 * \return contents string, or false in case of an error. 00663 */ 00664 function fetchContents() 00665 { 00666 $filePath = $this->filePath; 00667 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileFetchContents( '$filePath' )" ); 00668 $contents = $this->backend->_fetchContents( $filePath ); 00669 return $contents; 00670 } 00671 00672 /** 00673 * Returns file metadata. 00674 * 00675 * \public 00676 */ 00677 function stat() 00678 { 00679 eZDebugSetting::writeDebug( 'kernel-clustering', "db::stat()" ); 00680 if ( $this->metaData === false ) 00681 $this->loadMetaData(); 00682 return $this->metaData; 00683 } 00684 00685 /** 00686 * Returns file size. 00687 * 00688 * \public 00689 */ 00690 function size() 00691 { 00692 eZDebugSetting::writeDebug( 'kernel-clustering', "db::size()" ); 00693 00694 if ( $this->metaData === false ) 00695 $this->loadMetaData(); 00696 return isset( $this->metaData['size'] ) ? $this->metaData['size'] : null; 00697 } 00698 00699 /** 00700 * Returns file modification time. 00701 * 00702 * \public 00703 */ 00704 function mtime() 00705 { 00706 eZDebugSetting::writeDebug( 'kernel-clustering', "db::mtime()" ); 00707 00708 if ( $this->metaData === false ) 00709 $this->loadMetaData(); 00710 return isset( $this->metaData['mtime'] ) ? $this->metaData['mtime'] : null; 00711 } 00712 00713 /** 00714 * Returns file name. 00715 * 00716 * \public 00717 */ 00718 function name() 00719 { 00720 eZDebugSetting::writeDebug( 'kernel-clustering', "db::name()" ); 00721 00722 return $this->filePath; 00723 } 00724 00725 /** 00726 * \public 00727 * \static 00728 * \sa fileDeleteByWildcard() 00729 */ 00730 function fileDeleteByRegex( $dir, $fileRegex ) 00731 { 00732 $dir = eZDBFileHandler::cleanPath( $dir ); 00733 $fileRegex = eZDBFileHandler::cleanPath( $fileRegex ); 00734 eZDebug::writeWarning( "Using eZDBFileHandler::fileDeleteByRegex is not recommended since it has some severe performance issues" ); 00735 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDeleteByRegex( '$dir', '$fileRegex' )" ); 00736 00737 $regex = '^' . ( $dir ? $dir . '/' : '' ) . $fileRegex; 00738 $this->backend->_deleteByRegex( $regex ); 00739 } 00740 00741 /** 00742 * \public 00743 * \static 00744 * \sa fileDeleteByRegex() 00745 */ 00746 function fileDeleteByWildcard( $wildcard ) 00747 { 00748 $wildcard = eZDBFileHandler::cleanPath( $wildcard ); 00749 eZDebug::writeWarning( "Using eZDBFileHandler::fileDeleteByWildcard is not recommended since it has some severe performance issues" ); 00750 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDeleteByWildcard( '$wildcard' )" ); 00751 00752 $this->backend->_deleteByWildcard( $wildcard ); 00753 } 00754 00755 /** 00756 * \public 00757 * \static 00758 */ 00759 function fileDeleteByDirList( $dirList, $commonPath, $commonSuffix ) 00760 { 00761 foreach ( $dirList as $key => $dirItem ) 00762 { 00763 $dirList[$key] = eZDBFileHandler::cleanPath( $dirItem ); 00764 00765 } 00766 $commonPath = eZDBFileHandler::cleanPath( $commonPath ); 00767 $commonSuffix = eZDBFileHandler::cleanPath( $commonSuffix ); 00768 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDeleteByDirList( '$dirList', '$commonPath', '$commonSuffix' )" ); 00769 00770 $this->backend->_deleteByDirList( $dirList, $commonPath, $commonSuffix ); 00771 } 00772 00773 /** 00774 * Deletes specified file/directory. 00775 * 00776 * If a directory specified it is deleted recursively. 00777 * 00778 * \public 00779 * \static 00780 */ 00781 function fileDelete( $path, $fnamePart = false ) 00782 { 00783 $path = eZDBFileHandler::cleanPath( $path ); 00784 $fnamePart = eZDBFileHandler::cleanPath( $fnamePart ); 00785 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDelete( '$path' )" ); 00786 00787 if ( $fnamePart === false ) 00788 $this->backend->_delete( $path ); 00789 if ( $fnamePart !== false ) 00790 $pattern = $path . '/' . $fnamePart . '%'; 00791 else 00792 $pattern = $path . '/%'; 00793 $this->backend->_deleteByLike( $pattern ); 00794 } 00795 00796 /** 00797 * Deletes specified file/directory. 00798 * 00799 * If a directory specified it is deleted recursively. 00800 * 00801 * \public 00802 * \static 00803 */ 00804 function delete() 00805 { 00806 $path = $this->filePath; 00807 eZDebugSetting::writeDebug( 'kernel-clustering', "db::delete( '$path' )" ); 00808 00809 $this->backend->_delete( $path ); 00810 $this->backend->_deleteByLike( $path . '/%' ); 00811 00812 $this->metaData = false; 00813 } 00814 00815 /** 00816 * Deletes a file that has been fetched before. 00817 * 00818 * \public 00819 * \static 00820 */ 00821 function fileDeleteLocal( $path ) 00822 { 00823 $path = eZDBFileHandler::cleanPath( $path ); 00824 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDeleteLocal( '$path' )" ); 00825 00826 @unlink( $path ); 00827 } 00828 00829 /** 00830 * Deletes a file that has been fetched before. 00831 * 00832 * \public 00833 */ 00834 function deleteLocal() 00835 { 00836 $path = $this->filePath; 00837 eZDebugSetting::writeDebug( 'kernel-clustering', "db::deleteLocal( '$path' )" ); 00838 @unlink( $path ); 00839 } 00840 00841 /*! 00842 Purge local and remote file data for current file. 00843 */ 00844 function purge( $printCallback = false, $microsleep = false, $max = false, $expiry = false ) 00845 { 00846 $file = $this->filePath; 00847 if ( $max === false ) 00848 $max = 100; 00849 $count = 0; 00850 do 00851 { 00852 if ( $count > 0 && $microsleep ) 00853 usleep( $microsleep ); // Sleep a bit to make the database happier 00854 $count = $this->backend->_purgeByLike( $file . "/%", true, $max, $expiry, 'purge' ); 00855 $this->backend->_purge( $file, true, $expiry, 'purge' ); 00856 if ( $printCallback ) 00857 call_user_func_array( $printCallback, 00858 array( $file, $count ) ); 00859 } while ( $count > 0 ); 00860 // Remove local copy 00861 if ( is_file( $file ) ) 00862 { 00863 @unlink( $file ); 00864 } 00865 else if ( is_dir( $file ) ) 00866 { 00867 //include_once( 'lib/ezfile/classes/ezdir.php' ); 00868 eZDir::recursiveDelete( $file ); 00869 } 00870 } 00871 00872 /** 00873 * Check if given file/dir exists. 00874 * 00875 * \public 00876 * \static 00877 */ 00878 function fileExists( $path ) 00879 { 00880 $path = eZDBFileHandler::cleanPath( $path ); 00881 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileExists( '$path' )" ); 00882 00883 $rc = $this->backend->_exists( $path ); 00884 return $rc; 00885 } 00886 00887 /** 00888 * Check if given file/dir exists. 00889 * 00890 * NOTE: this function does not interact with database. 00891 * Instead, it just returns existance status determined in the constructor. 00892 * 00893 * \public 00894 */ 00895 function exists() 00896 { 00897 $path = $this->filePath; 00898 eZDebugSetting::writeDebug( 'kernel-clustering', "db::exists( '$path' )" ); 00899 $rc = $this->backend->_exists( $path ); 00900 return $rc; 00901 } 00902 00903 /** 00904 * Outputs file contents prepending them with appropriate HTTP headers. 00905 * 00906 * \public 00907 * \deprecated This function should not be used since it cannot handle reading errors. 00908 * For the PHP 5 port this should be removed. 00909 */ 00910 function passthrough() 00911 { 00912 $path = $this->filePath; 00913 eZDebugSetting::writeDebug( 'kernel-clustering', "db::passthrough( '$path' )" ); 00914 if ( $this->metaData === false ) 00915 $this->loadMetaData(); 00916 $size = $this->metaData['size']; 00917 $mimeType = $this->metaData['datatype']; 00918 $mtime = $this->metaData['mtime']; 00919 $mdate = gmdate( 'D, d M Y H:i:s T', $mtime ); 00920 00921 header( "Content-Length: $size" ); 00922 header( "Content-Type: $mimeType" ); 00923 header( "Last-Modified: $mdate GMT" ); 00924 header( "Expires: ". gmdate('D, d M Y H:i:s', time() + 6000) . ' GMT'); 00925 header( "Connection: close" ); 00926 header( "X-Powered-By: eZ Publish" ); 00927 header( "Accept-Ranges: bytes" ); 00928 00929 $this->backend->_passThrough( $path ); 00930 } 00931 00932 /** 00933 * Copy file. 00934 * 00935 * \public 00936 * \static 00937 */ 00938 function fileCopy( $srcPath, $dstPath ) 00939 { 00940 $srcPath = eZDBFileHandler::cleanPath( $srcPath ); 00941 $dstPath = eZDBFileHandler::cleanPath( $dstPath ); 00942 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileCopy( '$srcPath', '$dstPath' )" ); 00943 00944 $this->backend->_copy( $srcPath, $dstPath ); 00945 } 00946 00947 /** 00948 * Create symbolic or hard link to file. 00949 * 00950 * \public 00951 * \static 00952 */ 00953 function fileLinkCopy( $srcPath, $dstPath, $symLink ) 00954 { 00955 $srcPath = eZDBFileHandler::cleanPath( $srcPath ); 00956 $dstPath = eZDBFileHandler::cleanPath( $dstPath ); 00957 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileLinkCopy( '$srcPath', '$dstPath' )" ); 00958 00959 $this->backend->_linkCopy( $srcPath, $dstPath ); 00960 } 00961 00962 /** 00963 * Move file. 00964 * 00965 * \public 00966 * \static 00967 */ 00968 function fileMove( $srcPath, $dstPath ) 00969 { 00970 $srcPath = eZDBFileHandler::cleanPath( $srcPath ); 00971 $dstPath = eZDBFileHandler::cleanPath( $dstPath ); 00972 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileMove( '$srcPath', '$dstPath' )" ); 00973 00974 $this->backend->_rename( $srcPath, $dstPath ); 00975 00976 $this->metaData = false; 00977 } 00978 00979 /** 00980 * Move file. 00981 * 00982 * \public 00983 */ 00984 function move( $dstPath ) 00985 { 00986 $dstPath = eZDBFileHandler::cleanPath( $dstPath ); 00987 $srcPath = $this->filePath; 00988 00989 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileMove( '$srcPath', '$dstPath' )" ); 00990 00991 $this->backend->_rename( $srcPath, $dstPath ); 00992 00993 $this->metaData = false; 00994 } 00995 00996 /** 00997 * Get list of files stored in database. 00998 * 00999 * Used in bin/php/clusterize.php. 01000 */ 01001 function getFileList( $skipBinaryFiles = false, $skipImages = false ) 01002 { 01003 eZDebugSetting::writeDebug( 'kernel-clustering', 01004 sprintf( "db::getFileList( %d, %d )", 01005 (int) $skipBinaryFiles, (int) $skipImages ) ); 01006 return $this->backend->_getFileList( $skipBinaryFiles, $skipImages ); 01007 } 01008 01009 /*! 01010 \static 01011 Returns a clean version of input $path. 01012 01013 - Backslashes are turned into slashes. 01014 - Multiple consecutive slashes are turned into one slash. 01015 - Ending slashes are removed. 01016 01017 \example 01018 my\windows\path => my/windows/path 01019 extra//slashes/\are/fixed => extra/slashes/are/fixed 01020 ending/slashes/ => ending/slashes 01021 \endexample 01022 */ 01023 static function cleanPath( $path ) 01024 { 01025 if ( !is_string( $path ) ) 01026 return $path; 01027 return preg_replace( array( "#[/\\\\]+#", "#/$#" ), 01028 array( "/", "" ), 01029 $path ); 01030 } 01031 01032 /// vars 01033 public $backend; 01034 public $backendVerify; 01035 public $filePath; 01036 public $metaData; 01037 } 01038 01039 ?>