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