MediaWiki  REL1_22
FileCacheBase.php
Go to the documentation of this file.
00001 <?php
00029 abstract class FileCacheBase {
00030     protected $mKey;
00031     protected $mType = 'object';
00032     protected $mExt = 'cache';
00033     protected $mFilePath;
00034     protected $mUseGzip;
00035     /* lazy loaded */
00036     protected $mCached;
00037 
00038     /* @todo configurable? */
00039     const MISS_FACTOR = 15; // log 1 every MISS_FACTOR cache misses
00040     const MISS_TTL_SEC = 3600; // how many seconds ago is "recent"
00041 
00042     protected function __construct() {
00043         global $wgUseGzip;
00044 
00045         $this->mUseGzip = (bool)$wgUseGzip;
00046     }
00047 
00052     final protected function baseCacheDirectory() {
00053         global $wgFileCacheDirectory;
00054         return $wgFileCacheDirectory;
00055     }
00056 
00061     abstract protected function cacheDirectory();
00062 
00067     protected function cachePath() {
00068         if ( $this->mFilePath !== null ) {
00069             return $this->mFilePath;
00070         }
00071 
00072         $dir = $this->cacheDirectory();
00073         # Build directories (methods include the trailing "/")
00074         $subDirs = $this->typeSubdirectory() . $this->hashSubdirectory();
00075         # Avoid extension confusion
00076         $key = str_replace( '.', '%2E', urlencode( $this->mKey ) );
00077         # Build the full file path
00078         $this->mFilePath = "{$dir}/{$subDirs}{$key}.{$this->mExt}";
00079         if ( $this->useGzip() ) {
00080             $this->mFilePath .= '.gz';
00081         }
00082 
00083         return $this->mFilePath;
00084     }
00085 
00090     public function isCached() {
00091         if ( $this->mCached === null ) {
00092             $this->mCached = file_exists( $this->cachePath() );
00093         }
00094         return $this->mCached;
00095     }
00096 
00101     public function cacheTimestamp() {
00102         $timestamp = filemtime( $this->cachePath() );
00103         return ( $timestamp !== false )
00104             ? wfTimestamp( TS_MW, $timestamp )
00105             : false;
00106     }
00107 
00114     public function isCacheGood( $timestamp = '' ) {
00115         global $wgCacheEpoch;
00116 
00117         if ( !$this->isCached() ) {
00118             return false;
00119         }
00120 
00121         $cachetime = $this->cacheTimestamp();
00122         $good = ( $timestamp <= $cachetime && $wgCacheEpoch <= $cachetime );
00123         wfDebug( __METHOD__ . ": cachetime $cachetime, touched '{$timestamp}' epoch {$wgCacheEpoch}, good $good\n" );
00124 
00125         return $good;
00126     }
00127 
00132     protected function useGzip() {
00133         return $this->mUseGzip;
00134     }
00135 
00140     public function fetchText() {
00141         if ( $this->useGzip() ) {
00142             $fh = gzopen( $this->cachePath(), 'rb' );
00143             return stream_get_contents( $fh );
00144         } else {
00145             return file_get_contents( $this->cachePath() );
00146         }
00147     }
00148 
00153     public function saveText( $text ) {
00154         global $wgUseFileCache;
00155 
00156         if ( !$wgUseFileCache ) {
00157             return false;
00158         }
00159 
00160         if ( $this->useGzip() ) {
00161             $text = gzencode( $text );
00162         }
00163 
00164         $this->checkCacheDirs(); // build parent dir
00165         if ( !file_put_contents( $this->cachePath(), $text, LOCK_EX ) ) {
00166             wfDebug( __METHOD__ . "() failed saving " . $this->cachePath() . "\n" );
00167             $this->mCached = null;
00168             return false;
00169         }
00170 
00171         $this->mCached = true;
00172         return $text;
00173     }
00174 
00179     public function clearCache() {
00180         wfSuppressWarnings();
00181         unlink( $this->cachePath() );
00182         wfRestoreWarnings();
00183         $this->mCached = false;
00184     }
00185 
00190     protected function checkCacheDirs() {
00191         wfMkdirParents( dirname( $this->cachePath() ), null, __METHOD__ );
00192     }
00193 
00201     protected function typeSubdirectory() {
00202         return $this->mType . '/';
00203     }
00204 
00210     protected function hashSubdirectory() {
00211         global $wgFileCacheDepth;
00212 
00213         $subdir = '';
00214         if ( $wgFileCacheDepth > 0 ) {
00215             $hash = md5( $this->mKey );
00216             for ( $i = 1; $i <= $wgFileCacheDepth; $i++ ) {
00217                 $subdir .= substr( $hash, 0, $i ) . '/';
00218             }
00219         }
00220 
00221         return $subdir;
00222     }
00223 
00229     public function incrMissesRecent( WebRequest $request ) {
00230         global $wgMemc;
00231         if ( mt_rand( 0, self::MISS_FACTOR - 1 ) == 0 ) {
00232             # Get a large IP range that should include the user  even if that
00233             # person's IP address changes
00234             $ip = $request->getIP();
00235             if ( !IP::isValid( $ip ) ) {
00236                 return;
00237             }
00238             $ip = IP::isIPv6( $ip )
00239                 ? IP::sanitizeRange( "$ip/32" )
00240                 : IP::sanitizeRange( "$ip/16" );
00241 
00242             # Bail out if a request already came from this range...
00243             $key = wfMemcKey( get_class( $this ), 'attempt', $this->mType, $this->mKey, $ip );
00244             if ( $wgMemc->get( $key ) ) {
00245                 return; // possibly the same user
00246             }
00247             $wgMemc->set( $key, 1, self::MISS_TTL_SEC );
00248 
00249             # Increment the number of cache misses...
00250             $key = $this->cacheMissKey();
00251             if ( $wgMemc->get( $key ) === false ) {
00252                 $wgMemc->set( $key, 1, self::MISS_TTL_SEC );
00253             } else {
00254                 $wgMemc->incr( $key );
00255             }
00256         }
00257     }
00258 
00263     public function getMissesRecent() {
00264         global $wgMemc;
00265         return self::MISS_FACTOR * $wgMemc->get( $this->cacheMissKey() );
00266     }
00267 
00271     protected function cacheMissKey() {
00272         return wfMemcKey( get_class( $this ), 'misses', $this->mType, $this->mKey );
00273     }
00274 }