|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing the eZFS2FileHandler 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 * This class implements the new version of the FS file handler. 00013 * It provides support for stalecache, but can not be used on every platform: 00014 * support for unlink(), widely used, is not available on windows platforms 00015 * before PHP 5.3. If you use windows, you can not use this handler unless you 00016 * use this PHP version (beta at this time). 00017 * It is perfectly safe to use it on linux / unix 00018 * 00019 * @property-read cacheType 00020 */ 00021 class eZFS2FileHandler extends eZFSFileHandler 00022 { 00023 function __construct( $filePath = false ) 00024 { 00025 parent::__construct( $filePath ); 00026 if ( !isset( $GLOBALS['eZFS2FileHandler'] ) ) 00027 { 00028 $siteINI = eZINI::instance(); 00029 $GLOBALS['eZFS2FileHandler']['GenerationTimeout'] = $siteINI->variable( "ContentSettings", "CacheGenerationTimeout" ); 00030 unset( $siteINI ); 00031 00032 $fileINI = eZINI::instance( 'file.ini' ); 00033 $GLOBALS['eZFS2FileHandler']['NonExistantStaleCacheHandling'] = $fileINI->variable( "ClusteringSettings", "NonExistantStaleCacheHandling" ); 00034 unset( $fileINI ); 00035 } 00036 $this->generationTimeout = $GLOBALS['eZFS2FileHandler']['GenerationTimeout']; 00037 $this->nonExistantStaleCacheHandling = $GLOBALS['eZFS2FileHandler']['NonExistantStaleCacheHandling']; 00038 } 00039 00040 /** 00041 * Creates a single transaction out of the typical file operations for accessing caches. 00042 * Caches are normally ready from the database or local file, if the entry does not exist 00043 * or is expired then it generates the new cache data and stores it. 00044 * This method takes care of these operations and handles the custom code by performing 00045 * callbacks when needed. 00046 * 00047 * The $retrieveCallback is used when the file contents can be used (ie. not re-generation) and 00048 * is called when the file is ready locally. 00049 * The function will be called with the file path as the first parameter, the mtime as the second 00050 * and optionally $extraData as the third. 00051 * The function must return the file contents or an instance of eZClusterFileFailure which can 00052 * be used to tell the system that the retrieve data cannot be used after all. 00053 * $retrieveCallback can be set to null which makes the system go directly to the generation. 00054 * 00055 * The $generateCallback is used when the file content is expired or does not exist, in this 00056 * case the content must be re-generated and stored. 00057 * The function will be called with the file path as the first parameter and optionally $extraData 00058 * as the second. 00059 * The function must return an array with information on the contents, the array consists of: 00060 * - scope - The current scope of the file, is optional. 00061 * - datatype - The current datatype of the file, is optional. 00062 * - content - The file content, this can be any type except null. 00063 * - binarydata - The binary data which is written to the file. 00064 * - 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. 00065 * Note: Set $generateCallback to false to disable generation callback. 00066 * 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(). 00067 * 00068 * Either *content* or *binarydata* must be supplied, if not an error is issued and it returns null. 00069 * If *content* is set it will be used as the return value of this function, if not it will return the binary data. 00070 * 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. 00071 * 00072 * For convenience the $generateCallback function can return a string which will be considered as the 00073 * binary data for the file and returned as the content. 00074 * 00075 * For controlling how long a cache entry can be used the parameters $expiry and $ttl is used. 00076 * $expiry can be set to a timestamp which controls the absolute max time for the cache, after this 00077 * time/date the cache will never be used. If the value is set to a negative value or null there the 00078 * expiration check is disabled. 00079 * 00080 * $ttl (time to live) tells how many seconds the cache can live from the time it was stored. If the 00081 * value is set to negative or null there is no limit for the lifetime of the cache. A value of 0 means 00082 * that the cache will always expire and practically disables caching. 00083 * For the cache to be used both the $expiry and $ttl check must hold. 00084 * 00085 * @param mixed $retrieveCallback 00086 * @param mixed $generateCallback 00087 * @param int $ttl 00088 * @param int $expiry 00089 * @param array $extraData 00090 */ 00091 public function processCache( $retrieveCallback, $generateCallback = null, $ttl = null, $expiry = null, $extraData = null ) 00092 { 00093 $curtime = time(); 00094 $tries = 0; 00095 $noCache = false; 00096 00097 if ( $expiry < 0 ) 00098 $expiry = null; 00099 if ( $ttl < 0 ) 00100 $ttl = null; 00101 00102 while ( true ) 00103 { 00104 $forceGeneration = false; 00105 $storeCache = true; 00106 00107 // a local, non expired cache file exists (it may still be expired by 00108 // expiry timestamp, this is tested by the retrieve callback) 00109 if ( $retrieveCallback !== null && !$this->isExpired( $expiry, $curtime, $ttl ) ) 00110 { 00111 $mtime = @filemtime( $this->filePath ); 00112 $args = array( $this->filePath, $mtime ); 00113 if ( $extraData !== null ) 00114 $args[] = $extraData; 00115 $retval = call_user_func_array( $retrieveCallback, $args ); 00116 if ( !( $retval instanceof eZClusterFileFailure ) ) 00117 { 00118 return $retval; 00119 } 00120 00121 // if the retrieve callback returns an cluster failure, 00122 // data have to be generated 00123 $forceGeneration = true; 00124 } 00125 // stale cache mode has been triggered, we use the old cache file 00126 // if it exists 00127 elseif ( $this->useStaleCache ) 00128 { 00129 // a stalecache file exists 00130 if ( $this->exists() ) 00131 { 00132 $args = array( $this->filePath, $curtime ); 00133 if ( $extraData !== null ) 00134 $args[] = $extraData; 00135 $retval = call_user_func_array( $retrieveCallback, $args ); 00136 if ( !( $retval instanceof eZClusterFileFailure ) ) 00137 { 00138 return $retval; 00139 } 00140 $forceGeneration = true; 00141 } 00142 // no stalecache file exists, what we do depends on settings 00143 else 00144 { 00145 // generate the dynamic data without storage 00146 if ( $this->nonExistantStaleCacheHandling[ $this->cacheType ] == 'generate' ) 00147 { 00148 eZDebugSetting::writeDebug( 'kernel-clustering', $this->filePath, "Generation is being processed, generating own version" ); 00149 break; 00150 } 00151 // wait for the generating process to be finished (or timedout) 00152 else 00153 { 00154 // write a specific wait loop that uses the max generation time as a limit 00155 eZDebugSetting::writeDebug( 'kernel-clustering', "Generation of '$this->filePath' is being processed, waiting", __METHOD__ ); 00156 $waitedSeconds = 0; 00157 while ( $waitedSeconds < $this->remainingCacheGenerationTime ) 00158 { 00159 sleep( 1 ); 00160 $this->loadMetaData( true ); 00161 if ( $this->exists() ) 00162 { 00163 eZDebugSetting::writeDebug( 'kernel-clustering', "'$this->filePath' has been generated while waiting, using this newer file", __METHOD__ ); 00164 $this->useStaleCache = false; 00165 $this->remainingCacheGenerationTime = false; 00166 00167 // this continues to the main loop 'while (true)' 00168 continue 2; 00169 } 00170 $waitedSeconds++; 00171 } 00172 00173 // if we reached this point, it means that we are over the estimated timeout value 00174 // we try to take the generation over by starting the cache generation. IF this 00175 // fails again, it is probably because another waiting process has taken the generation 00176 // over. Maybe add a counter here to prevent some kind of death loop ? 00177 eZDebugSetting::writeDebug( 'kernel-clustering', "The cache file '$this->filePath' was not generated during the WAIT loop, restarting process", __METHOD__ ); 00178 $this->useStaleCache = false; 00179 $this->remainingCacheGenerationTime = false; 00180 $tries++; 00181 00182 // this continues to the main loop 'while (true)' 00183 continue; 00184 } 00185 } 00186 } 00187 00188 if ( $tries >= 2 ) 00189 { 00190 eZDebugSetting::writeDebug( 'kernel-clustering', "Reading was retried $tries times and reached the maximum, forcing generation", __METHOD__ ); 00191 $forceGeneration = true; // We will now generate the cache but not store it 00192 $storeCache = false; // This disables the cache storage 00193 } 00194 00195 // Generation part starts here 00196 if ( isset( $retval ) && 00197 $retval instanceof eZClusterFileFailure ) 00198 { 00199 // This specific error means that the retrieve callback told 00200 // us NOT to enter generation mode and therefore NOT to store this 00201 // cache. 00202 // This parameter will then be passed to the generate callback, 00203 // and this will set store to false 00204 if ( $retval->errno() == 3 ) 00205 { 00206 $noCache = true; 00207 } 00208 // check for non-expiry error codes 00209 elseif ( $retval->errno() != 1 ) 00210 { 00211 eZDebug::writeError( "Failed to retrieve data from callback: " . print_r( $retrieveCallback, true ), __METHOD__ ); 00212 return null; 00213 } 00214 $message = $retval->message(); 00215 if ( strlen( $message ) > 0 ) 00216 { 00217 eZDebugSetting::writeDebug( 'kernel-clustering', $retval->message(), __METHOD__ ); 00218 } 00219 // the retrieved data was expired so we need to generate it, let's continue 00220 } 00221 00222 // We need to lock if we have a generate-callback or 00223 // the generation is deferred to the caller. 00224 // Note: false means no generation 00225 if ( $generateCallback !== false ) 00226 { 00227 if ( !$this->useStaleCache and !$noCache ) 00228 { 00229 $res = $this->startCacheGeneration(); 00230 00231 // an integer means that the file is being generated by another process 00232 // and we should use an old cache if available. 00233 // We do this by setting $useStaleCache 00234 // to true, and restarting the loop from the beggining 00235 if ( $res !== true ) 00236 { 00237 eZDebugSetting::writeDebug( 'kernel-clustering', "{$this->filePath} is being generated, switching to staleCache mode", __METHOD__ ); 00238 $this->useStaleCache = true; 00239 $this->remainingCacheGenerationTime = $res; 00240 continue; 00241 } 00242 } 00243 } 00244 00245 // File in DB is outdated or non-existing, call write-callback to generate content 00246 if ( $generateCallback ) 00247 { 00248 $args = array( $this->filePath ); 00249 if ( $noCache ) 00250 $extraData['noCache'] = true; 00251 if ( $extraData !== null ) 00252 $args[] = $extraData; 00253 $fileData = call_user_func_array( $generateCallback, $args ); 00254 00255 if( !isset( $fileData['store'] ) ) 00256 $storeCache = false; 00257 else 00258 $storeCache = $fileData['store']; 00259 00260 return $this->storeCache( $fileData, $storeCache ); 00261 } 00262 00263 break; 00264 } 00265 00266 return new eZClusterFileFailure( 2, "Manual generation of file data is required, calling storeCache is required" ); 00267 } 00268 00269 /** 00270 * Stores the data in $fileData to the remote and local file and commits the 00271 * transaction. 00272 * The parameter $fileData must contain the same as information as the 00273 * $generateCallback returns as explained in processCache(). 00274 * @note This method is just a continuation of the code in processCache() and 00275 * is not meant to be called alone since it relies on specific state in 00276 * the database. 00277 */ 00278 function storeCache( $fileData, $storeCache = true ) 00279 { 00280 $scope = false; 00281 $datatype = false; 00282 $binaryData = null; 00283 $fileContent = null; 00284 $store = true; 00285 if ( is_array( $fileData ) ) 00286 { 00287 if ( isset( $fileData['scope'] ) ) 00288 $scope = $fileData['scope']; 00289 if ( isset( $fileData['datatype'] ) ) 00290 $datatype = $fileData['datatype']; 00291 if ( isset( $fileData['content'] ) ) 00292 $fileContent = $fileData['content']; 00293 if ( isset( $fileData['binarydata'] ) ) 00294 $binaryData = $fileData['binarydata']; 00295 if ( isset( $fileData['store'] ) ) 00296 $store = $fileData['store']; 00297 } 00298 else 00299 $binaryData = $fileData; 00300 00301 // if generation timedout, the .generating file's timeout has changed, 00302 // and we must not store the cache file 00303 if ( $store and !$this->checkCacheGenerationTimeout() ) 00304 $storeCache = false; 00305 00306 $mtime = false; 00307 $result = null; 00308 if ( $binaryData === null && 00309 $fileContent === null ) 00310 { 00311 eZDebug::writeError( "Write callback need to set the 'content' or 'binarydata' entry" ); 00312 return null; 00313 } 00314 00315 if ( $binaryData === null ) 00316 $binaryData = "<" . "?php\n\treturn ". var_export( $fileContent, true ) . ";\n?" . ">\n"; 00317 00318 if ( $fileContent === null ) 00319 $result = $binaryData; 00320 else 00321 $result = $fileContent; 00322 00323 // stale cache handling: we just return the result, no lock has been set 00324 if ( $this->useStaleCache ) 00325 { 00326 eZDebugSetting::writeDebug( 'kernel-clustering', "Returning locally generated data without storing", __METHOD__ ); 00327 return $result; 00328 } 00329 00330 if ( !$storeCache ) 00331 { 00332 $this->abortCacheGeneration(); 00333 return $result; 00334 } 00335 00336 // Store content locally 00337 $this->storeContents( $binaryData, $scope, $datatype, true ); 00338 00339 // we end the cache generation process, so that the .generating file 00340 // is renamed to its final name 00341 $this->endCacheGeneration(); 00342 00343 return $result; 00344 } 00345 00346 /** 00347 * Starts cache generation for the current file. 00348 * 00349 * This is done by creating a file named by the original file name, prefixed 00350 * with '.generating'. 00351 * 00352 * @todo add timeout handling... 00353 * 00354 * @return mixed true if generation lock was granted, an integer matching the 00355 * time before the current generation times out 00356 */ 00357 public function startCacheGeneration() 00358 { 00359 eZDebugSetting::writeDebug( "kernel-clustering", $this->filePath, __METHOD__ ); 00360 00361 $ret = true; 00362 00363 $generatingFilePath = $this->filePath . '.generating'; 00364 00365 // the x flag will throw a warning if the file exists. Allows existence 00366 // check AND creation at the same time 00367 if ( !$fp = @fopen( $generatingFilePath, 'x' ) ) 00368 { 00369 $directory = dirname( $generatingFilePath ) . DIRECTORY_SEPARATOR; 00370 00371 // the directory we're trying to create the file in does not exist 00372 eZDebugSetting::writeDebug( 'kernel-clustering', $this->filePath . " creation failed, checking if '$directory' exists", __METHOD__ ); 00373 00374 if ( !file_exists( $directory ) ) 00375 { 00376 eZDebugSetting::writeDebug( 'kernel-clustering', $this->filePath . " target directory does not exist, creating and trying again", __METHOD__ ); 00377 00378 if ( eZDir::mkdir( $directory, false, true ) ) 00379 eZDebugSetting::writeDebug( 'kernel-clustering', "Directory '$directory' created, trying to start generation again", __METHOD__ ); 00380 else 00381 eZDebugSetting::writeDebug( 'kernel-clustering', "Directory '$directory' failed to be created, it might have been created by another process", __METHOD__ ); 00382 00383 // we check again since the folder may have been created by another 00384 // process in between. Not likely, though. 00385 if ( !$fp = @fopen( $generatingFilePath, 'x' ) ) 00386 { 00387 $ret = $this->remainingCacheGenerationTime( $generatingFilePath ); 00388 } 00389 } 00390 // directory exists, we now check for timeout 00391 else 00392 { 00393 // timeout check 00394 if ( $mtime = @filemtime( $generatingFilePath ) ) 00395 { 00396 $remainingGenerationTime = $this->remainingCacheGenerationTime( $generatingFilePath ); 00397 eZDebugSetting::writeDebug( 'kernel-clustering', "Remaining generation time: $remainingGenerationTime", __METHOD__ ); 00398 if ( $remainingGenerationTime < 0 ) 00399 { 00400 eZDebugSetting::writeDebug( 'kernel-clustering', $this->filePath . " generating file exists, but generation has timedout, taking over", __METHOD__ ); 00401 touch( $generatingFilePath, time(), time() ); 00402 } 00403 else 00404 { 00405 eZDebugSetting::writeDebug( 'kernel-clustering', $this->filePath . " failed opening file for writing, generation is underway", __METHOD__ ); 00406 $ret = $remainingGenerationTime; 00407 } 00408 } 00409 } 00410 } 00411 00412 // if the generation lock was granted, we can perform our specific file 00413 // operations: change the file name to the generation name, and store 00414 // required generating informations 00415 if ( $ret === true ) 00416 { 00417 // $fp might not be a valid handle if timeout has occured 00418 if ( $fp ) 00419 fclose( $fp ); 00420 00421 eZClusterFileHandler::addGeneratingFile( $this ); 00422 $this->realFilePath = $this->filePath; 00423 $this->filePath = $generatingFilePath; 00424 $this->generationStartTimestamp = filemtime( $this->filePath ); 00425 } 00426 00427 return $ret; 00428 } 00429 00430 /** 00431 * Ends the cache generation started by startCacheGeneration(). 00432 * 00433 * If $rename is set to true (default), the .generating file is renamed and 00434 * overwrites the real file. 00435 * If set to false, the .generating file is removed, and the real file made 00436 * available. 00437 * 00438 * True should be used when actual data is stored in the standard file and 00439 * not the .generating one, for instance when using image alias generation. 00440 * 00441 * @param bool $rename Rename (true) or delete (false) the generating file 00442 * 00443 * @return bool 00444 */ 00445 public function endCacheGeneration( $rename = true) 00446 { 00447 if ( $this->realFilePath === null ) 00448 { 00449 $this->loadMetaData(); 00450 eZDebugSetting::writeDebug( 'kernel-clustering', "$this->filePath is not generating", "fs2::endCacheGeneration( '{$this->filePath}' )" ); 00451 return false; 00452 } 00453 00454 eZDebug::accumulatorStart( 'dbfile', false, 'dbfile' ); 00455 00456 $ret = false; 00457 eZDebugSetting::writeDebug( "kernel-clustering", $this->realFilePath, __METHOD__ ); 00458 00459 // rename the file to its final name 00460 if ( $rename === true ) 00461 { 00462 if ( eZFile::rename( $this->filePath, $this->realFilePath, false, eZFile::CLEAN_ON_FAILURE ) ) 00463 { 00464 $this->filePath = $this->realFilePath; 00465 $this->realFilePath = null; 00466 eZClusterFileHandler::removeGeneratingFile( $this ); 00467 $this->remainingCacheGenerationTime = false; 00468 $ret = true; 00469 $this->loadMetaData(); 00470 } 00471 else 00472 { 00473 eZLog::write( "eZFS2FileHandler::endCacheGeneration: Failed renaming '$this->filePath' to '$this->realFilePath'", 'cluster.log' ); 00474 } 00475 } 00476 else 00477 { 00478 unlink( $this->filePath ); 00479 $this->filePath = $this->realFilePath; 00480 $this->realFilePath = null; 00481 $this->loadMetaData(); 00482 } 00483 00484 eZDebug::accumulatorStop( 'dbfile' ); 00485 00486 return $ret; 00487 } 00488 00489 /** 00490 * Aborts the current cache generation process. 00491 * 00492 * Does so by rolling back the current transaction, which should be the 00493 * .generating file lock 00494 */ 00495 public function abortCacheGeneration() 00496 { 00497 eZDebugSetting::writeDebug( 'kernel-clustering', $this->realFilePath, __METHOD__ ); 00498 @unlink( $this->filePath ); 00499 $this->filePath = $this->realFilePath; 00500 $this->realFilePath = null; 00501 $this->remainingCacheGenerationTime = false; 00502 eZClusterFileHandler::removeGeneratingFile( $this ); 00503 } 00504 00505 /** 00506 * Checks if the .generating file was changed, which would mean that generation 00507 * timed out. If not timed out, refreshes the timestamp so that storage won't 00508 * be stolen 00509 */ 00510 public function checkCacheGenerationTimeout() 00511 { 00512 clearstatcache(); 00513 // file_exists = false: another process stole the lock and finished the generation 00514 // filemtime != stored one: another process is generating the file 00515 if ( !file_exists( $this->filePath ) or ( @filemtime( $this->filePath ) != $this->generationStartTimestamp ) ) 00516 { 00517 eZDebugSetting::writeDebug( 'kernel-clustering', "'$this->filePath' was changed during generation, looks like a generation timeout", __METHOD__ ); 00518 eZLog::write( "Generation of '$this->filePath' timed out", 'cluster.log' ); 00519 return false; 00520 } 00521 else 00522 { 00523 $mtime = time(); 00524 touch( $this->filePath, $mtime, $mtime ); 00525 return true; 00526 } 00527 } 00528 00529 /** 00530 * Returns the remaining time, in seconds, before the generating file times 00531 * out 00532 * 00533 * @param string $filePath 00534 * 00535 * @return int Remaining generation seconds. A negative value indicates a timeout. 00536 */ 00537 private function remainingCacheGenerationTime( $filePath ) 00538 { 00539 clearstatcache(); 00540 eZDebugSetting::writeDebug( 'kernel-clustering', "clearstatcache called on $filePath", __METHOD__ ); 00541 $mtime = @filemtime( $filePath ); 00542 $remainingGenerationTime = ( $mtime + $this->generationTimeout ) - time(); 00543 return $remainingGenerationTime; 00544 } 00545 00546 /** 00547 * Delete files located in a directories from dirList, with common prefix specified by 00548 * commonPath, and common suffix with added wildcard at the end 00549 * 00550 * \public 00551 * \static 00552 * \sa fileDeleteByRegex() 00553 */ 00554 function fileDeleteByDirList( $dirList, $commonPath, $commonSuffix ) 00555 { 00556 $dirs = implode( ',', $dirList ); 00557 $wildcard = $commonPath .'/{' . $dirs . '}/' . $commonSuffix . '*'; 00558 00559 eZDebugSetting::writeDebug( 'kernel-clustering', "fs::fileDeleteByDirList( '".implode(', ', $dirList)."', '$commonPath', '$commonSuffix' )", __METHOD__ ); 00560 00561 eZDebug::accumulatorStart( 'dbfile', false, 'dbfile' ); 00562 array_map( array( __CLASS__, '_expire' ), eZSys::globBrace( $wildcard ) ); 00563 eZDebug::accumulatorStop( 'dbfile' ); 00564 } 00565 00566 /** 00567 * Deletes the file(s) or directory matching $path and $fnamePart if given 00568 * @param $path path of the file to delete 00569 * @param $fnamePart path of the file to delete 00570 */ 00571 function fileDelete( $path, $fnamePart = false ) 00572 { 00573 eZDebugSetting::writeDebug( 'kernel-clustering', "fs::fileDelete( '$path' )", __METHOD__ ); 00574 00575 eZDebug::accumulatorStart( 'dbfile', false, 'dbfile' ); 00576 00577 $list = array(); 00578 if ( $fnamePart !== false ) 00579 { 00580 $list = glob( $path . "/" . $fnamePart . "*" ); 00581 } 00582 else 00583 { 00584 $list = array( $path ); 00585 } 00586 00587 foreach ( $list as $path ) 00588 { 00589 if ( is_file( $path ) ) 00590 { 00591 self::_expire( $path ); 00592 } 00593 else 00594 { 00595 self::_recursiveExpire( $path ); 00596 } 00597 } 00598 00599 eZDebug::accumulatorStop( 'dbfile' ); 00600 } 00601 00602 /** 00603 * Deletes specified file/directory. 00604 * 00605 * If a directory specified it is deleted recursively. 00606 * 00607 * \public 00608 * \static 00609 */ 00610 function delete() 00611 { 00612 $path = $this->filePath; 00613 eZDebugSetting::writeDebug( 'kernel-clustering', "fs::delete( '$path' )", __METHOD__ ); 00614 00615 eZDebug::accumulatorStart( 'dbfile', false, 'dbfile' ); 00616 00617 if ( is_file( $path ) ) 00618 { 00619 self::_expire( $path ); 00620 } 00621 elseif ( is_dir( $path ) ) 00622 { 00623 self::_recursiveExpire( $path ); 00624 } 00625 00626 eZDebug::accumulatorStop( 'dbfile' ); 00627 } 00628 00629 /** 00630 * Deletes a file that has been fetched before. 00631 * 00632 * In case of fetching from filesystem does nothing. 00633 * 00634 * \public 00635 */ 00636 function deleteLocal() 00637 { 00638 $path = $this->filePath; 00639 eZDebugSetting::writeDebug( 'kernel-clustering', "fs::deleteLocal( '$path' )", __METHOD__ ); 00640 00641 eZClusterFileHandler::cleanupEmptyDirectories( $path ); 00642 } 00643 00644 /** 00645 * Purge local and remote file data for current file. 00646 */ 00647 function purge( $printCallback = false, $microsleep = false, $max = false, $expiry = false ) 00648 { 00649 $file = $this->filePath; 00650 if ( $max === false ) 00651 $max = 100; 00652 $count = 0; 00653 if ( is_file( $file ) ) 00654 $list = array( $file ); 00655 else 00656 $list = glob( $file . "/*" ); 00657 do 00658 { 00659 if ( ( $count % $max ) == 0 && $microsleep ) 00660 usleep( $microsleep ); // Sleep a bit to make the filesystem happier 00661 00662 $count = 0; 00663 $file = array_shift( $list ); 00664 00665 if ( is_file( $file ) ) 00666 { 00667 $mtime = @filemtime( $file ); 00668 if ( $expiry === false || 00669 $mtime < $expiry ) // remove it if it is too old 00670 { 00671 @unlink( $file ); 00672 00673 eZClusterFileHandler::cleanupEmptyDirectories( $file ); 00674 } 00675 ++$count; 00676 } 00677 else if ( is_dir( $file ) ) 00678 { 00679 $list = array_merge( $list, glob( $file . "/*" ) ); 00680 } 00681 00682 if ( $printCallback ) 00683 call_user_func_array( $printCallback, 00684 array( $file, 1 ) ); 00685 } while ( count( $list ) > 0 ); 00686 } 00687 00688 /** 00689 * Expire the given file 00690 * @param string $path Path of the file to expire 00691 * @return bool 00692 */ 00693 private static function _expire( $path ) 00694 { 00695 eZDebugSetting::writeDebug( 'kernel-clustering', $path, __METHOD__ ); 00696 $ret = touch( $path, self::EXPIRY_TIMESTAMP, self::EXPIRY_TIMESTAMP ); 00697 eZDebugSetting::writeDebug( 'kernel-clustering', date( 'd/m/Y', filemtime( $path ) ), __METHOD__ . ': mtime' ); 00698 return $ret; 00699 } 00700 00701 /** 00702 * Expires all files in a directory 00703 * @param string $directory 00704 * @return void 00705 */ 00706 private static function _recursiveExpire( $directory ) 00707 { 00708 eZDebugSetting::writeDebug( 'kernel-clustering', $directory, __METHOD__ ); 00709 00710 if ( $handle = @opendir( $directory ) ) 00711 { 00712 while ( ( $file = readdir( $handle ) ) !== false ) 00713 { 00714 if ( ( $file == "." ) || ( $file == ".." ) ) 00715 { 00716 continue; 00717 } 00718 if ( is_dir( $directory . '/' . $file ) ) 00719 { 00720 self::_recursiveExpire( $directory . '/' . $file ); 00721 } 00722 else 00723 { 00724 self::_expire( $directory . '/' . $file ); 00725 } 00726 } 00727 @closedir( $handle ); 00728 } 00729 } 00730 00731 /** 00732 * Determines the cache type based on the path 00733 * @return string viewcache, cacheblock or misc 00734 */ 00735 private function _cacheType() 00736 { 00737 if ( strstr( $this->filePath, 'cache/content' ) !== false ) 00738 return 'viewcache'; 00739 elseif ( strstr( $this->filePath, 'cache/template-block' ) !== false ) 00740 return 'cacheblock'; 00741 else 00742 return 'misc'; 00743 } 00744 00745 /** 00746 * Magic getter 00747 */ 00748 function __get( $propertyName ) 00749 { 00750 switch ( $propertyName ) 00751 { 00752 case 'cacheType': 00753 { 00754 if ( $this->internalCacheType === null ) 00755 { 00756 $this->internalCacheType = $this->_cacheType(); 00757 } 00758 return $this->internalCacheType; 00759 } break; 00760 } 00761 } 00762 00763 /** 00764 * eZFS2 doesn't require clusterizing, as it only uses the filesystem 00765 * @return bool 00766 */ 00767 public function requiresClusterizing() 00768 { 00769 return false; 00770 } 00771 00772 /** 00773 * eZFS2 doesn't require purge as it already purges files in realtime 00774 * (FS based) 00775 * 00776 * @since 4.3 00777 * @deprecated Deprecated as of 4.5, use {@link eZFS2FileHandler::requiresPurge()} instead. 00778 * @return bool 00779 */ 00780 public function requiresBinaryPurge() 00781 { 00782 return false; 00783 } 00784 00785 /** 00786 * eZFS2 doesn't require purge as it already purges files in realtime 00787 * (FS based) 00788 * 00789 * @since 4.5.0 00790 * @return bool 00791 */ 00792 public function requiresPurge() 00793 { 00794 return false; 00795 } 00796 00797 public function hasStaleCacheSupport() 00798 { 00799 return true; 00800 } 00801 00802 /** 00803 * Checks if the given $path exists. 00804 * 00805 * @param string $path 00806 * @return bool 00807 */ 00808 function fileExists( $path ) 00809 { 00810 eZDebugSetting::writeDebug( 'kernel-clustering', "fs::fileExists( '$path' )", __METHOD__ ); 00811 00812 eZDebug::accumulatorStart( 'dbfile', false, 'dbfile' ); 00813 $rc = file_exists( $path ) && ( filemtime( $path ) != self::EXPIRY_TIMESTAMP ); 00814 eZDebug::accumulatorStop( 'dbfile' ); 00815 00816 return $rc; 00817 } 00818 00819 /** 00820 * Check if given file/dir exists. 00821 * 00822 * NOTE: this function does not interact with filesystem. 00823 * Instead, it just returns existance status determined in the constructor. 00824 * 00825 * \public 00826 */ 00827 function exists() 00828 { 00829 $path = $this->filePath; 00830 if ( isset( $this->metaData['mtime'] ) ) 00831 { 00832 $return = ( $this->metaData['mtime'] != self::EXPIRY_TIMESTAMP ); 00833 } 00834 else 00835 { 00836 $return = false; 00837 } 00838 eZDebugSetting::writeDebug( 'kernel-clustering', "fs2::exists( '$path' ): " . ( $return ? 'true' :'false' ), __METHOD__ ); 00839 return $return; 00840 } 00841 00842 00843 /** 00844 * holds the real file path. This is only used when we are generating a cache 00845 * file, in which case $filePath holds the generating cache file name, 00846 * and $realFilePath holds the real name 00847 */ 00848 public $realFilePath = null; 00849 00850 /** 00851 * Indicates that the current cache item is being generated and an old version 00852 * should be used 00853 * @var bool 00854 */ 00855 protected $useStaleCache = false; 00856 00857 /** 00858 * Generation timeout, in seconds. If a generating file exists for more than 00859 * $generationTimeout seconds, it is taken over 00860 * @var int 00861 */ 00862 protected $generationTimeout; 00863 00864 /** 00865 * Holds the preferences used when stale cache is activated and no expired 00866 * file is available. 00867 * This is loaded from file.ini, ClusteringSettings.NonExistantStaleCacheHandling 00868 */ 00869 protected $nonExistantStaleCacheHandling; 00870 00871 /** 00872 * Holds the number of seconds remaining before the generating cache times out 00873 * @var int 00874 */ 00875 protected $remainingCacheGenerationTime = false; 00876 00877 /** 00878 * When the instance generates the cached version for a file, this property 00879 * holds the timestamp at which generation was started. This is used to control 00880 * a possible generation timeout 00881 * @var int 00882 */ 00883 protected $generationStartTimestamp = false; 00884 00885 /** 00886 * Cached value of cache type 00887 * 00888 * @var string|null 00889 */ 00890 protected $internalCacheType = null; 00891 } 00892 ?>