eZ Publish  [trunk]
ezdbfilehandler.php
Go to the documentation of this file.
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 ?>