|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing the eZDBFileHandler 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 kernel 00009 */ 00010 00011 /*! 00012 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. 00013 */ 00014 00015 class eZDBFileHandler implements ezpDatabaseBasedClusterFileHandler 00016 { 00017 /*! 00018 Controls whether file data from database is cached on the local filesystem. 00019 \note This is primarily available for debugging purposes. 00020 */ 00021 const LOCAL_CACHE = 1; 00022 00023 /*! 00024 Controls the maximum number of metdata entries to keep in memory for this request. 00025 If the limit is reached the least used entries are removed. 00026 */ 00027 const INFOCACHE_MAX = 200; 00028 00029 /** 00030 * Constructor. 00031 * 00032 * $filePath File path. If specified, file metadata is fetched in the constructor. 00033 */ 00034 function __construct( $filePath = false ) 00035 { 00036 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00037 eZDebugSetting::writeDebug( 'kernel-clustering', "db::ctor( '$filePath' )" ); 00038 00039 if ( self::$dbbackend === null ) 00040 { 00041 $optionArray = array( 'iniFile' => 'file.ini', 00042 'iniSection' => 'ClusteringSettings', 00043 'iniVariable' => 'DBBackend' ); 00044 00045 $options = new ezpExtensionOptions( $optionArray ); 00046 00047 self::$dbbackend = eZExtension::getHandlerClass( $options ); 00048 self::$dbbackend->_connect( false ); 00049 00050 // connection failed 00051 if( self::$dbbackend->db === false ) 00052 throw new eZClusterHandlerDBNoConnectionException( self::$dbbackend->dbparams['host'], self::$dbbackend->dbparams['user'], self::$dbbackend->dbparams['pass'] ); 00053 } 00054 00055 $this->filePath = $filePath; 00056 00057 if ( !isset( $GLOBALS['eZDBFileHandler_Settings'] ) ) 00058 { 00059 $fileINI = eZINI::instance( 'file.ini' ); 00060 $GLOBALS['eZDBFileHandler_Settings']['NonExistantStaleCacheHandling'] = $fileINI->variable( "ClusteringSettings", "NonExistantStaleCacheHandling" ); 00061 unset( $fileINI ); 00062 } 00063 $this->nonExistantStaleCacheHandling = $GLOBALS['eZDBFileHandler_Settings']['NonExistantStaleCacheHandling']; 00064 $this->filePermissionMask = octdec( eZINI::instance()->variable( 'FileSettings', 'StorageFilePermissions' ) ); 00065 } 00066 00067 /** 00068 * Disconnects the cluster handler from the database 00069 */ 00070 public function disconnect() 00071 { 00072 self::$dbbackend->_disconnect(); 00073 self::$dbbackend = null; 00074 } 00075 00076 /** 00077 * Load file meta information from the database 00078 * @param bool $force File stats will be refreshed if true 00079 */ 00080 function loadMetaData( $force = false ) 00081 { 00082 // Fetch metadata. 00083 if ( $this->filePath === false ) 00084 return; 00085 00086 // we don't fetch metaData if self::metaData === false, since this means 00087 // we already tried and got no results, unless $force == true 00088 if ( ( $this->_metaData === false ) && ( $force !== true ) ) 00089 return; 00090 00091 if ( ( $force === true ) && isset( $GLOBALS['eZClusterInfo'][$this->filePath] ) ) 00092 unset( $GLOBALS['eZClusterInfo'][$this->filePath] ); 00093 00094 // Checks for metadata stored in memory, useful for repeated access 00095 // to the same file in one request 00096 // TODO: On PHP5 turn into static member 00097 if ( isset( $GLOBALS['eZClusterInfo'][$this->filePath] ) ) 00098 { 00099 $GLOBALS['eZClusterInfo'][$this->filePath]['cnt'] += 1; 00100 $this->_metaData = $GLOBALS['eZClusterInfo'][$this->filePath]['data']; 00101 return; 00102 } 00103 00104 $metaData = self::$dbbackend->_fetchMetadata( $this->filePath ); 00105 if ( $metaData ) 00106 $this->_metaData = $metaData; 00107 else 00108 $this->_metaData = false; 00109 00110 // Clean up old entries if the maximum count is reached 00111 if ( isset( $GLOBALS['eZClusterInfo'] ) && 00112 count( $GLOBALS['eZClusterInfo'] ) >= self::INFOCACHE_MAX ) 00113 { 00114 uasort( $GLOBALS['eZClusterInfo'], 00115 create_function( '$a, $b', 00116 '$a=$a["cnt"]; $b=$b["cnt"]; if ( $a > $b ) return -1; else if ( $a == $b ) return 0; else return 1;' ) ); 00117 array_pop( $GLOBALS['eZClusterInfo'] ); 00118 } 00119 if ( !isset( $GLOBALS['eZClusterInfo'] ) ) 00120 $GLOBALS['eZClusterInfo'] = array(); 00121 $GLOBALS['eZClusterInfo'][$this->filePath] = array( 'cnt' => 1, 00122 'data' => $metaData ); 00123 } 00124 00125 /** 00126 * Stores a local file to the cluster 00127 * 00128 * @param string $filePath Path to the file being stored. 00129 * @param string $scope File scope. Used to group similar files together. Examples: image, template-block... 00130 * @param string $delete true if the file should be deleted after storing. 00131 * @param string $datatype File mime type 00132 * 00133 * @return void 00134 */ 00135 function fileStore( $filePath, $scope = false, $delete = false, $datatype = false ) 00136 { 00137 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00138 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileStore( '$filePath' )" ); 00139 00140 if ( $scope === false ) 00141 $scope = 'UNKNOWN_SCOPE'; 00142 00143 if ( $datatype === false ) 00144 $datatype = 'misc'; 00145 00146 self::$dbbackend->_store( $filePath, $datatype, $scope ); 00147 00148 if ( $delete ) 00149 @unlink( $filePath ); 00150 } 00151 00152 /** 00153 * Store file contents. 00154 * 00155 * @param string $filePath 00156 * @param mixed $contents 00157 * @param string $scope 00158 * @param string $datatype 00159 * 00160 * @return void 00161 */ 00162 function fileStoreContents( $filePath, $contents, $scope = false, $datatype = false ) 00163 { 00164 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00165 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileStoreContents( '$filePath' )" ); 00166 00167 if ( $scope === false ) 00168 $scope = 'UNKNOWN_SCOPE'; 00169 00170 if ( $datatype === false ) 00171 $datatype = 'misc'; 00172 00173 self::$dbbackend->_storeContents( $filePath, $contents, $scope, $datatype ); 00174 } 00175 00176 /** 00177 * Store file contents. 00178 * 00179 * \public 00180 * 00181 * \param $storeLocally If true the file will also be stored on the local file system. 00182 */ 00183 function storeContents( $contents, $scope = false, $datatype = false, $storeLocally = false ) 00184 { 00185 if ( $scope === false ) 00186 $scope = 'UNKNOWN_SCOPE'; 00187 00188 if ( $datatype === false ) 00189 $datatype = 'misc'; 00190 00191 $filePath = $this->filePath; 00192 eZDebugSetting::writeDebug( 'kernel-clustering', "db::storeContents( '$filePath' )" ); 00193 self::$dbbackend->_storeContents( $filePath, $contents, $scope, $datatype ); 00194 if ( $storeLocally ) 00195 { 00196 eZFile::create( basename( $filePath ), dirname( $filePath ), $contents, true ); 00197 } 00198 } 00199 00200 /** 00201 * Fetches file from db and saves it in FS under the same name. 00202 * 00203 * \public 00204 * \static 00205 */ 00206 function fileFetch( $filePath ) 00207 { 00208 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00209 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileFetch( '$filePath' )" ); 00210 00211 $fetchReturn = self::$dbbackend->_fetch( $filePath ); 00212 00213 $this->fixPermissions( $filePath ); 00214 00215 return $fetchReturn; 00216 } 00217 00218 /** 00219 * Creates a single transaction out of the typical file operations for accessing caches. 00220 * Caches are normally ready from the database or local file, if the entry does not exist 00221 * or is expired then it generates the new cache data and stores it. 00222 * This method takes care of these operations and handles the custom code by performing 00223 * callbacks when needed. 00224 * 00225 * Either *content* or *binarydata* must be supplied, if not an error is issued and it returns null. 00226 * If *content* is set it will be used as the return value of this function, if not it will return the binary data. 00227 * If *binarydata* is set it will be used as the binary data for the file, if not it will perform a var_export on 00228 * *content* and use that as the binary data. 00229 * 00230 * For controlling how long a cache entry can be used the parameters $expiry and $ttl is used. 00231 * 00232 * @param mixed $retrieveCallback 00233 * The $retrieveCallback is used when the file contents can be used (ie. not re-generation) and 00234 * is called when the file is ready locally. 00235 * The function will be called with the file path as the first parameter, the mtime as the second 00236 * and optionally $extraData as the third. 00237 * The function must return the file contents or an instance of eZClusterFileFailure which can 00238 * be used to tell the system that the retrieve data cannot be used after all. 00239 * $retrieveCallback can be set to null which makes the system go directly to the generation. 00240 * Set to null to tell the function to perform a write lock but not do any generation, the generation must 00241 * done be done by the caller by calling storeCache(). 00242 * @param mixed $generateCallback 00243 * used when the file content is expired or does not exist, in this case the content must be re-generated and 00244 * stored. The function will be called with the file path as the first parameter and optionally $extraData 00245 * as the second. 00246 * Set to false to disable generation callback. 00247 * For convenience the $generateCallback function can return a string which will be considered as the 00248 * binary data for the file and returned as the content. 00249 * @param mixed $ttl 00250 * (time to live) tells how many seconds the cache can live from the time it was stored. If the 00251 * value is set to negative or null there is no limit for the lifetime of the cache. A value of 0 means 00252 * that the cache will always expire and practically disables caching. 00253 * For the cache to be used both the $expiry and $ttl check must hold. 00254 * @param mixed $expiry 00255 * $expiry can be set to a timestamp which controls the absolute max time for the cache, after this 00256 * time/date the cache will never be used. If the value is set to a negative value or null there the 00257 * expiration check is disabled. 00258 * @param mixed $extraData Extra parameters to be sent to {@link $generateCallback} and {@link $retrieveCallback} 00259 * 00260 * @return array an array with information on the contents, the array consists of: 00261 * - scope: The current scope of the file, is optional. 00262 * - datatype: The current datatype of the file, is optional. 00263 * - content: The file content, this can be any type except null. 00264 * - binarydata: The binary data which is written to the file. 00265 * - store: Whether *content* or *binarydata* should be stored to the file, if false it will simply 00266 * return the data. Optional, by default it is true. 00267 */ 00268 function processCache( $retrieveCallback, $generateCallback = null, $ttl = null, $expiry = null, $extraData = null ) 00269 { 00270 $forceDB = false; 00271 $timestamp = null; 00272 $curtime = time(); 00273 $tries = 0; 00274 $noCache = false; 00275 00276 if ( $expiry < 0 ) 00277 $expiry = null; 00278 if ( $ttl < 0 ) 00279 $ttl = null; 00280 00281 // Main loop 00282 while ( true ) 00283 { 00284 // Start read checks 00285 // Note: The while loop is used to make it easier to break out of the "read" code 00286 while ( true ) 00287 { 00288 // No retrieve method so go directly to generate+store 00289 if ( $retrieveCallback === null || !$this->filePath ) 00290 break; 00291 00292 if ( !self::LOCAL_CACHE ) 00293 { 00294 $forceDB = true; 00295 } 00296 else 00297 { 00298 if ( $this->isLocalFileExpired( $expiry, $curtime, $ttl ) ) 00299 { 00300 00301 // if we are in stale cache mode, we only forceDB if the 00302 // file does not exist at all 00303 if ( $this->useStaleCache ) 00304 { 00305 if ( !file_exists( $this->filePath ) ) 00306 { 00307 eZDebugSetting::writeDebug( 'kernel-clustering', 00308 "Local file '{$this->filePath}' does not exist and can not be used for stale cache. Checking with DB", __METHOD__ ); 00309 $forceDB = true; 00310 00311 // forceDB + useStaleCache means that we should check for the DB file. 00312 } 00313 } 00314 else 00315 { 00316 // Local file is older than global timestamp, check with DB 00317 eZDebugSetting::writeDebug( 'kernel-clustering', 00318 "Local file (mtime=" . @filemtime( $this->filePath ) . ") is older than timestamp ($expiry) and ttl($ttl), check with DB", __METHOD__ ); 00319 $forceDB = true; 00320 } 00321 } 00322 } 00323 00324 if ( $this->metaData === null ) 00325 $this->loadMetaData(); 00326 00327 if ( !$forceDB ) 00328 { 00329 // check if DB file is deleted 00330 if ( !$this->useStaleCache && ( $this->metaData === false || $this->metaData['mtime'] < 0 ) ) 00331 { 00332 if ( $generateCallback !== false ) 00333 eZDebugSetting::writeDebug( 'kernel-clustering', 00334 "Database file is deleted, need to regenerate data", __METHOD__ ); 00335 else 00336 eZDebugSetting::writeDebug( 'kernel-clustering', 00337 "Database file is deleted, cannot get data", __METHOD__ ); 00338 break; 00339 } 00340 00341 // check if FS file is older than DB file 00342 if ( !$this->useStaleCache && $this->isLocalFileExpired( $this->metaData['mtime'], $curtime, $ttl ) ) 00343 { 00344 eZDebugSetting::writeDebug( 'kernel-clustering', 00345 "Local file (mtime=" . @filemtime( $this->filePath ) . ") is older than DB, checking with DB", __METHOD__ ); 00346 $forceDB = true; 00347 } 00348 else 00349 { 00350 if ( $this->useStaleCache ) 00351 { 00352 // to get the retrieve callback to accept the cache file, 00353 // we force its mtime to the current time 00354 $mtime = $curtime; 00355 eZDebugSetting::writeDebug( 'kernel-clustering', "Processing local stale cache file {$this->filePath}", __METHOD__ ); 00356 } 00357 else 00358 { 00359 $mtime = filemtime( $this->filePath ); 00360 eZDebugSetting::writeDebug( 'kernel-clustering', "Processing local cache file {$this->filePath}", __METHOD__ ); 00361 } 00362 00363 $args = array( $this->filePath, $mtime ); 00364 if ( $extraData !== null ) 00365 $args[] = $extraData; 00366 $retval = call_user_func_array( $retrieveCallback, $args ); 00367 if ( $retval instanceof eZClusterFileFailure ) 00368 { 00369 break; 00370 } 00371 return $retval; 00372 } 00373 } 00374 00375 if ( $forceDB ) 00376 { 00377 // stale cache, and no DB or FS file available 00378 if ( $this->useStaleCache && $this->metaData === false ) 00379 { 00380 // configuration says we have to generate our own version 00381 if ( $this->nonExistantStaleCacheHandling[ $this->cacheType ] == 'generate' ) 00382 { 00383 // no cache available, but a generate callback exists, skip to generation 00384 if ( $generateCallback !== false ) 00385 { 00386 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file is deleted, need to regenerate data", __METHOD__ ); 00387 break; 00388 } 00389 // if no generate callback exists, we can directly skip the main loop 00390 else 00391 { 00392 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file is deleted, cannot get data", __METHOD__ ); 00393 break 2; 00394 } 00395 } 00396 // wait for the generating process to be finished (or timedout) 00397 else 00398 { 00399 while ( $this->remainingCacheGenerationTime-- >= 0 ) 00400 { 00401 eZDebug::writeDebug( $this->remainingCacheGenerationTime, '$this->remainingCacheGenerationTime' ); 00402 // we don't know if the file gets generated on the current 00403 // frontend or not. However, we can still try the FS cache 00404 // first, then the DB cache if FS is not found, since this 00405 // will be much more efficient 00406 if ( !file_exists( $this->filePath ) ) 00407 { 00408 $this->loadMetaData( true ); 00409 if ( $this->metaData === false ) 00410 { 00411 sleep( 1 ); 00412 continue; 00413 } 00414 else 00415 { 00416 break; 00417 } 00418 } 00419 else 00420 { 00421 break; 00422 } 00423 } 00424 00425 // if we reached this point, it means that we are over the estimated timeout value 00426 // we try to take the generation over by starting the cache generation. IF this 00427 // fails again, it is probably because another waiting process has taken the generation 00428 // over. Maybe add a counter here to prevent some kind of death loop ? 00429 eZDebugSetting::writeDebug( 'kernel-clustering', "Checking if {$this->filePath} was generating during the wait loop", __METHOD__ ); 00430 $this->loadMetaData( true ); 00431 $this->useStaleCache = false; 00432 $this->remainingCacheGenerationTime = false; 00433 $forceDB = false; 00434 00435 // this continues to the main loop 'while (true)' 00436 continue 2; 00437 } 00438 } 00439 // no stale cache, and expired DB file 00440 elseif ( !$this->useStaleCache && ( $this->metaData === false || $this->isDBFileExpired( $expiry, $curtime, $ttl ) ) ) // no stalecache, and no DB file, generation is required 00441 { 00442 // no cache available, but a generate callback exists, skip to generation 00443 if ( $generateCallback !== false ) 00444 { 00445 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file is deleted, need to regenerate data", __METHOD__ ); 00446 00447 // we break out of one loop so that the generateCallback is called 00448 break; 00449 } 00450 // if no generate callback exists, we can directly skip the main loop 00451 else 00452 { 00453 eZDebugSetting::writeDebug( 'kernel-clustering', "Database file is deleted, cannot get data", __METHOD__ ); 00454 00455 // we break out of two loops so that we directly exit the method and have 00456 // the rest of execution generate the data 00457 break 2; 00458 } 00459 } 00460 else 00461 { 00462 eZDebugSetting::writeDebug( 'kernel-clustering', "Callback from DB file {$this->filePath}", __METHOD__ ); 00463 if ( self::LOCAL_CACHE ) 00464 { 00465 $this->fetch(); 00466 00467 // Figure out which mtime to use for new file, must be larger than 00468 // mtime in DB at least. 00469 $mtime = $this->metaData['mtime'] + 1; 00470 $localmtime = @filemtime( $this->filePath ); 00471 $mtime = max( $mtime, $localmtime ); 00472 touch( $this->filePath, $mtime, $mtime ); 00473 clearstatcache(); // Needed because of touch() call 00474 00475 $args = array( $this->filePath, $mtime ); 00476 if ( $extraData !== null ) 00477 $args[] = $extraData; 00478 $retval = call_user_func_array( $retrieveCallback, $args ); 00479 if ( $retval instanceof eZClusterFileFailure ) 00480 { 00481 break; 00482 } 00483 return $retval; 00484 } 00485 else 00486 { 00487 $uniquePath = $this->fetchUnique(); 00488 00489 $args = array( $uniquePath, $this->metaData['mtime'] ); 00490 if ( $extraData !== null ) 00491 $args[] = $extraData; 00492 $retval = call_user_func_array( $retrieveCallback, $args ); 00493 $this->fileDeleteLocal( $uniquePath ); 00494 if ( $retval instanceof eZClusterFileFailure ) 00495 break; 00496 return $retval; 00497 } 00498 } 00499 eZDebugSetting::writeDebug( 'kernel-clustering', 00500 "Database file does not exist, need to regenerate data", __METHOD__ ); 00501 break; 00502 } 00503 } 00504 00505 if ( $tries >= 2 ) 00506 { 00507 eZDebugSetting::writeDebug( 'kernel-clustering', 00508 "Reading was retried $tries times and reached the maximum, returning null", __METHOD__ ); 00509 return null; 00510 } 00511 00512 // Generation part starts here 00513 if ( isset( $retval ) && $retval instanceof eZClusterFileFailure ) 00514 { 00515 // this error means that the retrieve callback told 00516 // us NOT to enter generation mode and therefore NOT to store this 00517 // cache 00518 // This parameter will then be passed to the generate callback, 00519 // and this will set store to false 00520 if ( $retval->errno() == 3 ) 00521 { 00522 $noCache = true; 00523 } 00524 00525 // check for non-expiry error codes 00526 elseif ( $retval->errno() != 1 ) // check for non-expiry error codes 00527 { 00528 eZDebug::writeError( "Failed to retrieve data from callback", __METHOD__ ); 00529 return null; 00530 } 00531 $message = $retval->message(); 00532 if ( strlen( $message ) > 0 ) 00533 { 00534 eZDebugSetting::writeDebug( 'kernel-clustering', $retval->message(), __METHOD__ ); 00535 } 00536 // the retrieved data was expired so we need to generate it, let's continue 00537 } 00538 00539 // We need to lock if we have a generate-callback or 00540 // the generation is deferred to the caller. 00541 // Note: false means no generation, while null means that generation 00542 // is deferred to the processing that follows (f.i. cache-blocks) 00543 if ( $generateCallback !== false && $this->filePath ) 00544 { 00545 if ( !$noCache and !$this->useStaleCache ) 00546 { 00547 $res = $this->startCacheGeneration(); 00548 if ( $res !== true ) 00549 { 00550 eZDebugSetting::writeDebug( 'kernel-clustering', "{$this->filePath} is being generated, switching to staleCache mode", __METHOD__ ); 00551 $this->useStaleCache = true; 00552 $this->remainingCacheGenerationTime = $res; 00553 $forceDB = false; 00554 continue; 00555 } 00556 } 00557 00558 // File in DB is outdated or non-existing, call write-callback to generate content 00559 if ( $generateCallback ) 00560 { 00561 $args = array( $this->filePath ); 00562 if ( $noCache ) 00563 $extraData['noCache'] = true; 00564 if ( $extraData !== null ) 00565 $args[] = $extraData; 00566 $fileData = call_user_func_array( $generateCallback, $args ); 00567 return $this->storeCache( $fileData ); 00568 } 00569 } 00570 00571 break; 00572 } // End main loop 00573 00574 return new eZClusterFileFailure( 2, "Manual generation of file data is required, calling storeCache is required" ); 00575 } 00576 00577 /** 00578 * Calculates if the file data is expired or not. 00579 * 00580 * @param string $fname Name of file, available for easy debugging. 00581 * @param int $mtime Modification time of file, can be set to false if file does not exist. 00582 * @param int $expiry Time when file is to be expired, a value of -1 will disable this check. 00583 * @param int $curtime The current time to check against. 00584 * @param int $ttl Number of seconds the data can live, set to null to disable TTL. 00585 * @return bool 00586 */ 00587 public static function isFileExpired( $fname, $mtime, $expiry, $curtime, $ttl ) 00588 { 00589 if ( $mtime == false or $mtime < 0 ) 00590 { 00591 return true; 00592 } 00593 else if ( $ttl === null ) 00594 { 00595 $ret = $mtime < $expiry; 00596 return $ret; 00597 } 00598 else 00599 { 00600 $ret = $mtime < max( $expiry, $curtime - $ttl ); 00601 return $ret; 00602 } 00603 } 00604 00605 /** 00606 * Calculates if the current file data is expired or not. 00607 * 00608 * @param int $expiry Time when file is to be expired, a value of -1 will disable this check. 00609 * @param int $curtime The current time to check against. 00610 * @param int $ttl Number of seconds the data can live, set to null to disable TTL. 00611 * @return bool 00612 */ 00613 public function isExpired( $expiry, $curtime, $ttl ) 00614 { 00615 if ( $this->metaData === null ) 00616 $this->loadMetaData(); 00617 00618 return self::isFileExpired( $this->filePath, $this->metaData['mtime'], $expiry, $curtime, $ttl ); 00619 } 00620 00621 /** 00622 * Calculates if the local file is expired or not. 00623 * @param int $expiry Time when file is to be expired, a value of -1 will disable this check. 00624 * @param int $curtime The current time to check against. 00625 * @param int $ttl Number of seconds the data can live, set to null to disable TTL. 00626 * @return bool 00627 */ 00628 public function isLocalFileExpired( $expiry, $curtime, $ttl ) 00629 { 00630 return self::isFileExpired( $this->filePath, @filemtime( $this->filePath ), $expiry, $curtime, $ttl ); 00631 } 00632 00633 /** 00634 * Calculates if the DB file is expired or not. 00635 * @param int $expiry Time when file is to be expired, a value of -1 will disable this check. 00636 * @param int $curtime The current time to check against. 00637 * @param int $ttl Number of seconds the data can live, set to null to disable TTL. 00638 * @return bool 00639 */ 00640 public function isDBFileExpired( $expiry, $curtime, $ttl ) 00641 { 00642 $mtime = isset( $this->metaData['mtime'] ) ? $this->metaData['mtime'] : 0; 00643 return self::isFileExpired( $this->filePath, $mtime, $expiry, $curtime, $ttl ); 00644 } 00645 00646 /** 00647 * Stores the data in $fileData to the remote and local file and commits the transaction. 00648 * 00649 * This method is just a continuation of the code in processCache() and is not meant to be called alone since it 00650 * relies on specific state in the database. 00651 * 00652 * The parameter $fileData must contain the same as information as the $generateCallback returns as explained 00653 * in processCache(). 00654 */ 00655 function storeCache( $fileData ) 00656 { 00657 $scope = false; 00658 $datatype = false; 00659 $binaryData = null; 00660 $fileContent = null; 00661 $store = true; 00662 $storeCache = false; 00663 00664 if ( is_array( $fileData ) ) 00665 { 00666 if ( isset( $fileData['scope'] ) ) 00667 $scope = $fileData['scope']; 00668 if ( isset( $fileData['datatype'] ) ) 00669 $datatype = $fileData['datatype']; 00670 if ( isset( $fileData['content'] ) ) 00671 $fileContent = $fileData['content']; 00672 if ( isset( $fileData['binarydata'] ) ) 00673 $binaryData = $fileData['binarydata']; 00674 if ( isset( $fileData['store'] ) ) 00675 $store = $fileData['store']; 00676 } 00677 else 00678 $binaryData = $fileData; 00679 00680 // This checks if we entered timeout and got our generating file stolen 00681 // If this happens, we don't store our cache 00682 if ( $store and $this->checkCacheGenerationTimeout() ) 00683 $storeCache = true; 00684 00685 $mtime = false; 00686 $result = null; 00687 if ( $binaryData === null && 00688 $fileContent === null ) 00689 { 00690 eZDebug::writeError( "Write callback need to set the 'content' or 'binarydata' entry" ); 00691 $this->abortCacheGeneration(); 00692 return null; 00693 } 00694 00695 if ( $binaryData === null ) 00696 $binaryData = "<" . "?php\n\treturn ". var_export( $fileContent, true ) . ";\n?" . ">\n"; 00697 00698 if ( $fileContent === null ) 00699 $result = $binaryData; 00700 else 00701 $result = $fileContent; 00702 00703 if ( !$this->filePath ) 00704 return $result; 00705 00706 // no store advice, we just return the result 00707 if ( !$storeCache ) 00708 { 00709 $this->abortCacheGeneration(); 00710 return $result; 00711 } 00712 00713 // stale cache handling: we just return the result, no lock has been set 00714 if ( $this->useStaleCache ) 00715 { 00716 // we write the generated cache to disk if it does not exist yet, 00717 // to speed up the next uncached operation 00718 // This file will be overwritten by the real file 00719 clearstatcache(); 00720 if ( !file_exists( $this->filePath ) ) 00721 { 00722 eZDebugSetting::writeDebug( 'kernel-clustering', "Writing stale file content to local file {$this->filePath}" ); 00723 eZFile::create( basename( $this->filePath ), dirname( $this->filePath ), $binaryData, true ); 00724 } 00725 return $result; 00726 } 00727 00728 // Check if we are allowed to store the data, if not just return the result 00729 if ( !$store ) 00730 { 00731 $this->abortCacheGeneration(); 00732 return $result; 00733 } 00734 00735 eZDebugSetting::writeDebug( 'kernel-clustering', "Writing new file content to database for {$this->filePath}" ); 00736 $this->storeContents( $binaryData, $scope, $datatype, $storeLocally = false ); 00737 00738 // we end the cache generation process, so that the .generating file 00739 // is renamed to its final name 00740 $this->endCacheGeneration(); 00741 00742 // the generated file is written to disk 00743 if ( self::LOCAL_CACHE ) 00744 { 00745 // Store content also locally 00746 eZDebugSetting::writeDebug( 'kernel-clustering', "Writing new file content to local file {$this->filePath}" ); 00747 eZFile::create( basename( $this->filePath ), dirname( $this->filePath ), $binaryData, true ); 00748 } 00749 00750 return $result; 00751 } 00752 00753 /*! 00754 Provides access to the file contents by downloading the file locally and 00755 calling $callback with the local filename. The callback can then process the 00756 contents and return the data in the same way as in processCache(). 00757 Downloading is only done once so the local copy is kept, while updates to the 00758 remote DB entry is synced with the local one. 00759 00760 The parameters $expiry and $extraData is the same as for processCache(). 00761 00762 \note Unlike processCache() this returns null if the file cannot be accessed. 00763 */ 00764 function processFile( $callback, $expiry = false, $extraData = null ) 00765 { 00766 $result = $this->processCache( $callback, false, null, $expiry, $extraData ); 00767 if ( $result instanceof eZClusterFileFailure ) 00768 { 00769 return null; 00770 } 00771 return $result; 00772 } 00773 00774 /** 00775 * Fetches file from db and saves it in FS under unique name. 00776 * 00777 * @return string filename with path of a saved file. You can use this filename to get contents of file from filesystem. 00778 */ 00779 function fetchUnique( ) 00780 { 00781 $filePath = $this->filePath; 00782 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fetchUnique( '$filePath' )" ); 00783 00784 $fetchedFilePath = self::$dbbackend->_fetch( $filePath, true ); 00785 $this->uniqueName = $fetchedFilePath; 00786 return $fetchedFilePath; 00787 } 00788 00789 /** 00790 * Fetches file from db and saves it in FS under the same name. 00791 * 00792 * \public 00793 */ 00794 function fetch( $noLocalCache = false ) 00795 { 00796 $filePath = $this->filePath; 00797 $metaData = self::$dbbackend->_fetchMetadata( $filePath ); 00798 $mtime = @filemtime( $filePath ); 00799 if ( !$noLocalCache || 00800 $metaData === false || 00801 $mtime === false || 00802 $mtime < $metaData['mtime'] || 00803 @filesize( $filePath ) != $metaData['size'] || 00804 !is_readable( $filePath ) ) 00805 { 00806 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fetch( '$filePath' )" ); 00807 self::$dbbackend->_fetch( $filePath ); 00808 } 00809 00810 $this->fixPermissions( $filePath ); 00811 } 00812 00813 /** 00814 * Returns file contents. 00815 * 00816 * \public 00817 * \static 00818 * \return contents string, or false in case of an error. 00819 */ 00820 function fileFetchContents( $filePath ) 00821 { 00822 $filePath = eZDBFileHandler::cleanPath( $filePath ); 00823 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileFetchContents( '$filePath' )" ); 00824 00825 $contents = self::$dbbackend->_fetchContents( $filePath ); 00826 return $contents; 00827 } 00828 00829 /** 00830 * Returns file contents. 00831 * 00832 * \public 00833 * \return contents string, or false in case of an error. 00834 */ 00835 function fetchContents() 00836 { 00837 $filePath = $this->filePath; 00838 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileFetchContents( '$filePath' )" ); 00839 $contents = self::$dbbackend->_fetchContents( $filePath ); 00840 return $contents; 00841 } 00842 00843 /** 00844 * Returns file metadata. 00845 * 00846 * \public 00847 */ 00848 function stat() 00849 { 00850 eZDebugSetting::writeDebug( 'kernel-clustering', "db::stat()" ); 00851 if ( $this->metaData === null ) 00852 $this->loadMetaData(); 00853 return $this->metaData; 00854 } 00855 00856 /** 00857 * Returns file size. 00858 * 00859 * \public 00860 */ 00861 function size() 00862 { 00863 eZDebugSetting::writeDebug( 'kernel-clustering', "db::size()" ); 00864 00865 if ( $this->metaData === null ) 00866 $this->loadMetaData(); 00867 return isset( $this->metaData['size'] ) ? $this->metaData['size'] : null; 00868 } 00869 00870 /** 00871 * Returns file modification time. 00872 * 00873 * \public 00874 */ 00875 function mtime() 00876 { 00877 eZDebugSetting::writeDebug( 'kernel-clustering', "db::mtime()" ); 00878 00879 if ( $this->metaData === null ) 00880 $this->loadMetaData(); 00881 return isset( $this->metaData['mtime'] ) ? (int)$this->metaData['mtime'] : null; 00882 } 00883 00884 /** 00885 * Returns file name. 00886 * 00887 * \public 00888 */ 00889 function name() 00890 { 00891 eZDebugSetting::writeDebug( 'kernel-clustering', "db::name()" ); 00892 00893 return $this->filePath; 00894 } 00895 00896 /** 00897 * \public 00898 * \static 00899 * \sa fileDeleteByWildcard() 00900 */ 00901 function fileDeleteByRegex( $dir, $fileRegex ) 00902 { 00903 $dir = eZDBFileHandler::cleanPath( $dir ); 00904 $fileRegex = eZDBFileHandler::cleanPath( $fileRegex ); 00905 eZDebug::writeWarning( "Using eZDBFileHandler::fileDeleteByRegex is not recommended since it has some severe performance issues" ); 00906 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDeleteByRegex( '$dir', '$fileRegex' )" ); 00907 00908 $regex = '^' . ( $dir ? $dir . '/' : '' ) . $fileRegex; 00909 self::$dbbackend->_deleteByRegex( $regex ); 00910 } 00911 00912 /** 00913 * \public 00914 * \static 00915 * \sa fileDeleteByRegex() 00916 */ 00917 function fileDeleteByWildcard( $wildcard ) 00918 { 00919 $wildcard = eZDBFileHandler::cleanPath( $wildcard ); 00920 eZDebug::writeWarning( "Using eZDBFileHandler::fileDeleteByWildcard is not recommended since it has some severe performance issues" ); 00921 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDeleteByWildcard( '$wildcard' )" ); 00922 00923 self::$dbbackend->_deleteByWildcard( $wildcard ); 00924 } 00925 00926 /** 00927 * \public 00928 * \static 00929 */ 00930 function fileDeleteByDirList( $dirList, $commonPath, $commonSuffix ) 00931 { 00932 foreach ( $dirList as $key => $dirItem ) 00933 { 00934 $dirList[$key] = eZDBFileHandler::cleanPath( $dirItem ); 00935 00936 } 00937 $commonPath = eZDBFileHandler::cleanPath( $commonPath ); 00938 $commonSuffix = eZDBFileHandler::cleanPath( $commonSuffix ); 00939 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDeleteByDirList( '" . join( ", ", $dirList ) . "', '$commonPath', '$commonSuffix' )" ); 00940 00941 self::$dbbackend->_deleteByDirList( $dirList, $commonPath, $commonSuffix ); 00942 } 00943 00944 /** 00945 * Deletes specified file/directory. 00946 * 00947 * If a directory specified it is deleted recursively. 00948 * 00949 * \public 00950 * \static 00951 */ 00952 function fileDelete( $path, $fnamePart = false ) 00953 { 00954 $path = eZDBFileHandler::cleanPath( $path ); 00955 $fnamePart = eZDBFileHandler::cleanPath( $fnamePart ); 00956 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDelete( '$path' )" ); 00957 00958 if ( $fnamePart === false ) 00959 { 00960 self::$dbbackend->_delete( $path ); 00961 } 00962 else 00963 { 00964 $pattern = $path . '/' . $fnamePart . '%'; 00965 self::$dbbackend->_deleteByLike( $pattern ); 00966 } 00967 } 00968 00969 /** 00970 * Deletes specified file/directory. 00971 * 00972 * If a directory specified it is deleted recursively. 00973 * 00974 * \public 00975 * \static 00976 */ 00977 function delete() 00978 { 00979 $path = $this->filePath; 00980 eZDebugSetting::writeDebug( 'kernel-clustering', "db::delete( '$path' )" ); 00981 00982 self::$dbbackend->_delete( $path ); 00983 00984 $this->_metaData = null; 00985 } 00986 00987 /** 00988 * Deletes a file that has been fetched before. 00989 * 00990 * \public 00991 * \static 00992 */ 00993 function fileDeleteLocal( $path ) 00994 { 00995 $path = eZDBFileHandler::cleanPath( $path ); 00996 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileDeleteLocal( '$path' )" ); 00997 00998 @unlink( $path ); 00999 01000 eZClusterFileHandler::cleanupEmptyDirectories( $path ); 01001 } 01002 01003 /** 01004 * Deletes a file that has been fetched before. 01005 * 01006 * \public 01007 */ 01008 function deleteLocal() 01009 { 01010 $path = $this->filePath; 01011 eZDebugSetting::writeDebug( 'kernel-clustering', "db::deleteLocal( '$path' )" ); 01012 @unlink( $path ); 01013 01014 eZClusterFileHandler::cleanupEmptyDirectories( $path ); 01015 } 01016 01017 /*! 01018 Purge local and remote file data for current file. 01019 */ 01020 function purge( $printCallback = false, $microsleep = false, $max = false, $expiry = false ) 01021 { 01022 $file = $this->filePath; 01023 if ( $max === false ) 01024 $max = 100; 01025 $count = 0; 01026 do 01027 { 01028 if ( $count > 0 && $microsleep ) 01029 usleep( $microsleep ); // Sleep a bit to make the database happier 01030 $count = self::$dbbackend->_purgeByLike( $file . "/%", true, $max, $expiry, 'purge' ); 01031 self::$dbbackend->_purge( $file, true, $expiry, 'purge' ); 01032 if ( $printCallback ) 01033 call_user_func_array( $printCallback, 01034 array( $file, $count ) ); 01035 } while ( $count > 0 ); 01036 // Remove local copy 01037 if ( is_file( $file ) ) 01038 { 01039 @unlink( $file ); 01040 } 01041 else if ( is_dir( $file ) ) 01042 { 01043 eZDir::recursiveDelete( $file ); 01044 } 01045 01046 eZClusterFileHandler::cleanupEmptyDirectories( $file ); 01047 } 01048 01049 /** 01050 * Check if given file/dir exists. 01051 * 01052 * \public 01053 * \static 01054 */ 01055 function fileExists( $path ) 01056 { 01057 $path = eZDBFileHandler::cleanPath( $path ); 01058 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileExists( '$path' )" ); 01059 01060 $rc = self::$dbbackend->_exists( $path ); 01061 return $rc; 01062 } 01063 01064 /** 01065 * Check if given file/dir exists. 01066 * 01067 * NOTE: this function does not interact with database. 01068 * Instead, it just returns existance status determined in the constructor. 01069 * 01070 * \public 01071 */ 01072 function exists() 01073 { 01074 $path = $this->filePath; 01075 eZDebugSetting::writeDebug( 'kernel-clustering', "db::exists( '$path' )" ); 01076 $rc = self::$dbbackend->_exists( $path ); 01077 return $rc; 01078 } 01079 01080 /** 01081 * Outputs file contents prepending them with appropriate HTTP headers. 01082 * 01083 * @param int $offset Transfer start offset 01084 * @param int $length Transfer length 01085 * 01086 * @return void 01087 */ 01088 function passthrough( $offset = 0, $length = false ) 01089 { 01090 $fname = "db::passthrough( '{$this->filePath}' )"; 01091 eZDebugSetting::writeDebug( 'kernel-clustering', $fname ); 01092 if ( $this->metaData === null ) 01093 $this->loadMetaData(); 01094 01095 self::$dbbackend->_passThrough( $this->filePath, $offset, $length, $fname ); 01096 } 01097 01098 /** 01099 * Copy file. 01100 * 01101 * \public 01102 * \static 01103 */ 01104 function fileCopy( $srcPath, $dstPath ) 01105 { 01106 $srcPath = eZDBFileHandler::cleanPath( $srcPath ); 01107 $dstPath = eZDBFileHandler::cleanPath( $dstPath ); 01108 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileCopy( '$srcPath', '$dstPath' )" ); 01109 01110 self::$dbbackend->_copy( $srcPath, $dstPath ); 01111 } 01112 01113 /** 01114 * Create symbolic or hard link to file. 01115 * 01116 * \public 01117 * \static 01118 */ 01119 function fileLinkCopy( $srcPath, $dstPath, $symLink ) 01120 { 01121 $srcPath = eZDBFileHandler::cleanPath( $srcPath ); 01122 $dstPath = eZDBFileHandler::cleanPath( $dstPath ); 01123 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileLinkCopy( '$srcPath', '$dstPath' )" ); 01124 01125 self::$dbbackend->_linkCopy( $srcPath, $dstPath ); 01126 } 01127 01128 /** 01129 * Move file. 01130 * 01131 * \public 01132 * \static 01133 */ 01134 function fileMove( $srcPath, $dstPath ) 01135 { 01136 $srcPath = eZDBFileHandler::cleanPath( $srcPath ); 01137 $dstPath = eZDBFileHandler::cleanPath( $dstPath ); 01138 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileMove( '$srcPath', '$dstPath' )" ); 01139 01140 self::$dbbackend->_rename( $srcPath, $dstPath ); 01141 01142 $this->_metaData = null; 01143 } 01144 01145 /** 01146 * Move file. 01147 * 01148 * \public 01149 */ 01150 function move( $dstPath ) 01151 { 01152 $dstPath = eZDBFileHandler::cleanPath( $dstPath ); 01153 $srcPath = $this->filePath; 01154 01155 eZDebugSetting::writeDebug( 'kernel-clustering', "db::fileMove( '$srcPath', '$dstPath' )" ); 01156 01157 self::$dbbackend->_rename( $srcPath, $dstPath ); 01158 01159 $this->_metaData = null; 01160 } 01161 01162 /** 01163 * Get list of files stored in database. 01164 * 01165 * Used in bin/php/clusterize.php. 01166 * 01167 * @param array $scopes return only files that belong to any of these scopes 01168 * @param boolean $excludeScopes if true, then reverse the meaning of $scopes, which is 01169 * return only files that do not belong to any of the scopes listed in $scopes 01170 */ 01171 function getFileList( $scopes = false, $excludeScopes = false ) 01172 { 01173 eZDebugSetting::writeDebug( 'kernel-clustering', 01174 sprintf( "db::getFileList( array( %s ), %d )", 01175 implode( ', ', $scopes ), (int) $excludeScopes ) ); 01176 return self::$dbbackend->_getFileList( $scopes, $excludeScopes ); 01177 } 01178 01179 /** 01180 * Returns a clean version of input $path. 01181 * 01182 * - Backslashes are turned into slashes. 01183 * - Multiple consecutive slashes are turned into one slash. 01184 * - Ending slashes are removed. 01185 * 01186 * Examples: 01187 * - my\windows\path => my/windows/path 01188 * - extra//slashes/\are/fixed => extra/slashes/are/fixed 01189 * - ending/slashes/ => ending/slashes 01190 */ 01191 static function cleanPath( $path ) 01192 { 01193 if ( !is_string( $path ) ) 01194 return $path; 01195 return preg_replace( array( "#[/\\\\]+#", "#/$#" ), 01196 array( "/", "" ), 01197 $path ); 01198 } 01199 01200 /** 01201 * Starts cache generation for the current file. 01202 * 01203 * This is done by creating a file named by the original file name, prefixed 01204 * with '.generating'. 01205 * 01206 * @return bool false if the file is being generated, true if it is not 01207 */ 01208 public function startCacheGeneration() 01209 { 01210 $generatingFilePath = $this->filePath . '.generating'; 01211 $ret = self::$dbbackend->_startCacheGeneration( $this->filePath, $generatingFilePath ); 01212 01213 // generation granted 01214 if ( $ret['result'] == 'ok' ) 01215 { 01216 eZClusterFileHandler::addGeneratingFile( $this ); 01217 $this->realFilePath = $this->filePath; 01218 $this->filePath = $generatingFilePath; 01219 $this->generationStartTimestamp = $ret['mtime']; 01220 return true; 01221 } 01222 // failure: the file is being generated 01223 elseif ( $ret['result'] == 'ko' ) 01224 { 01225 return $ret['remaining']; 01226 } 01227 // unhandled error case, should not happen 01228 else 01229 { 01230 eZLog::write( "An error occured starting cache generation on '$generatingFilePath'", 'cluster.log' ); 01231 return false; 01232 } 01233 } 01234 01235 /** 01236 * Ends the cache generation started by startCacheGeneration(). 01237 */ 01238 public function endCacheGeneration( $rename = true ) 01239 { 01240 if ( self::$dbbackend->_endCacheGeneration( $this->realFilePath, $this->filePath, $rename ) ) 01241 { 01242 $this->filePath = $this->realFilePath; 01243 $this->realFilePath = null; 01244 eZClusterFileHandler::removeGeneratingFile( $this ); 01245 return true; 01246 } 01247 else 01248 { 01249 return false; 01250 } 01251 } 01252 01253 /** 01254 * Aborts the current cache generation process. 01255 * 01256 * Does so by rolling back the current transaction, which should be the 01257 * .generating file lock 01258 */ 01259 public function abortCacheGeneration() 01260 { 01261 self::$dbbackend->_abortCacheGeneration( $this->filePath ); 01262 $this->filePath = $this->realFilePath; 01263 $this->realFilePath = null; 01264 eZClusterFileHandler::removeGeneratingFile( $this ); 01265 } 01266 01267 /** 01268 * Checks if the .generating file was changed, which would mean that generation 01269 * timed out. If not timed out, refreshes the timestamp so that storage won't 01270 * be stolen 01271 */ 01272 public function checkCacheGenerationTimeout() 01273 { 01274 return self::$dbbackend->_checkCacheGenerationTimeout( $this->filePath, $this->generationStartTimestamp ); 01275 } 01276 01277 /** 01278 * Determines the cache type based on the path 01279 * @return string viewcache, cacheblock or misc 01280 */ 01281 protected function computeCacheType() 01282 { 01283 if ( strpos( $this->filePath, 'cache/content' ) !== false ) 01284 return 'viewcache'; 01285 elseif ( strpos( $this->filePath, 'cache/template-block' ) !== false ) 01286 return 'cacheblock'; 01287 else 01288 return 'misc'; 01289 } 01290 01291 /** 01292 * Magic getter 01293 */ 01294 function __get( $propertyName ) 01295 { 01296 switch ( $propertyName ) 01297 { 01298 case 'cacheType': 01299 { 01300 static $cacheType = null; 01301 if ( $this->_cacheType == null ) 01302 $this->_cacheType = $this->computeCacheType(); 01303 return $this->_cacheType; 01304 } break; 01305 01306 case 'metaData': 01307 { 01308 if ( $this->_metaData === null ) 01309 { 01310 $this->loadMetaData(); 01311 } 01312 return $this->_metaData; 01313 } 01314 } 01315 } 01316 01317 /** 01318 * Since eZDB uses the database, running clusterize.php is required 01319 * @return bool 01320 */ 01321 public function requiresClusterizing() 01322 { 01323 return true; 01324 } 01325 01326 /** 01327 * eZDB does require binary purge. 01328 * It does store files in DB and therefore doesn't remove files in real time 01329 * 01330 * @since 4.3.0 01331 * @deprecated Deprecated as of 4.5, use {@link eZDBFileHandler::requiresPurge()} instead. 01332 * @return bool 01333 */ 01334 public function requiresBinaryPurge() 01335 { 01336 return true; 01337 } 01338 01339 /** 01340 * eZDB does require binary purge. 01341 * It does store files in DB and therefore doesn't remove files in real time 01342 * 01343 * @since 4.5.0 01344 * @return bool 01345 */ 01346 public function requiresPurge() 01347 { 01348 return true; 01349 } 01350 01351 /** 01352 * Fetches the first $limit expired binary items from the DB 01353 * 01354 * @param array $limit A 2 items array( offset, limit ) 01355 * 01356 * @return array(eZClusterFileHandlerInterace) 01357 * @since 4.3.0 01358 * @deprecated Deprecated as of 4.5, use {@link eZDBFileHandler::fetchExpiredItems()} instead. 01359 * 01360 * @todo handle output using $cli or something 01361 */ 01362 public function fetchExpiredBinaryItems( $limit = array( 0, 100 ) ) 01363 { 01364 return self::$dbbackend->fetchExpiredItems( array( 'image', 'binaryfile' ), $limit ); 01365 } 01366 01367 /** 01368 * Fetches the first $limit expired files from the DB 01369 * 01370 * @param array $scopes Array of scopes to fetch from 01371 * @param array $limit A 2 items array( offset, limit ) 01372 * @param int $expiry Number of seconds, only items older than this will be returned 01373 * 01374 * @return array(filepath) 01375 * @since 4.5.0 01376 */ 01377 public function fetchExpiredItems( $scopes, $limit = array( 0 , 100 ), $expiry = false ) 01378 { 01379 return self::$dbbackend->expiredFilesList( $scopes, $limit, $expiry ); 01380 } 01381 01382 public function hasStaleCacheSupport() 01383 { 01384 return true; 01385 } 01386 01387 protected function fixPermissions( $filePath ) 01388 { 01389 if ( file_exists( $filePath ) ) 01390 chmod( $filePath, $this->filePermissionMask ); 01391 } 01392 01393 /** 01394 * Database backend class 01395 * @var eZDBFileHandlerMysqlBackend 01396 */ 01397 public static $dbbackend; 01398 01399 /** 01400 * Path to the current file 01401 * @var string 01402 */ 01403 public $filePath; 01404 01405 /** 01406 * holds the real file path. This is only used when we are generating a cache 01407 * file, in which case $filePath holds the generating cache file name, 01408 * and $realFilePath holds the real name 01409 */ 01410 public $realFilePath = null; 01411 01412 /** 01413 * holds the file's metaData loaded from database 01414 * The variable's type indicates the exact status: 01415 * - null: means that metaData have not been loaded yet 01416 * - false: means that metaData were loaded but the file was not found in DB 01417 * - array: metaData have been loaded and file exists 01418 * @todo refactor to a magic property: 01419 * - when the property is requested, we check if it's null. 01420 * - if it is, we load the metadata from the database and cache them 01421 * - if it is not, we return the metaData 01422 * - then we add a reinitMetaData() method that resets the property to null 01423 * by erasing the cache 01424 */ 01425 public $_metaData = null; 01426 01427 /** 01428 * Indicates that the current cache item is being generated and an old version 01429 * should be used 01430 * @var bool 01431 */ 01432 protected $useStaleCache = false; 01433 01434 /** 01435 * Holds the preferences used when stale cache is activated and no expired 01436 * file is available. 01437 * This is loaded from file.ini, ClusteringSettings.NonExistantStaleCacheHandling 01438 */ 01439 protected $nonExistantStaleCacheHandling; 01440 01441 /** 01442 * Holds the number of seconds remaining before the generating cache times out 01443 * @var int 01444 */ 01445 protected $remainingCacheGenerationTime = false; 01446 01447 /** 01448 * When the instance generates the cached version for a file, this property 01449 * holds the timestamp at which generation was started. This is used to control 01450 * a possible generation timeout 01451 * @var int 01452 */ 01453 protected $generationStartTimestamp = false; 01454 01455 /** 01456 * Type of cache file, used by the nameTrunk feature to determine how nametrunk is computed 01457 * @var string 01458 */ 01459 protected $_cacheType; 01460 01461 /** 01462 * Permission mask that must be applied to created files 01463 * @var int 01464 */ 01465 private $filePermissionMask; 01466 01467 } 01468 ?>