MediaWiki  REL1_24
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 
00055         return $wgFileCacheDirectory;
00056     }
00057 
00062     abstract protected function cacheDirectory();
00063 
00068     protected function cachePath() {
00069         if ( $this->mFilePath !== null ) {
00070             return $this->mFilePath;
00071         }
00072 
00073         $dir = $this->cacheDirectory();
00074         # Build directories (methods include the trailing "/")
00075         $subDirs = $this->typeSubdirectory() . $this->hashSubdirectory();
00076         # Avoid extension confusion
00077         $key = str_replace( '.', '%2E', urlencode( $this->mKey ) );
00078         # Build the full file path
00079         $this->mFilePath = "{$dir}/{$subDirs}{$key}.{$this->mExt}";
00080         if ( $this->useGzip() ) {
00081             $this->mFilePath .= '.gz';
00082         }
00083 
00084         return $this->mFilePath;
00085     }
00086 
00091     public function isCached() {
00092         if ( $this->mCached === null ) {
00093             $this->mCached = file_exists( $this->cachePath() );
00094         }
00095 
00096         return $this->mCached;
00097     }
00098 
00103     public function cacheTimestamp() {
00104         $timestamp = filemtime( $this->cachePath() );
00105 
00106         return ( $timestamp !== false )
00107             ? wfTimestamp( TS_MW, $timestamp )
00108             : false;
00109     }
00110 
00117     public function isCacheGood( $timestamp = '' ) {
00118         global $wgCacheEpoch;
00119 
00120         if ( !$this->isCached() ) {
00121             return false;
00122         }
00123 
00124         $cachetime = $this->cacheTimestamp();
00125         $good = ( $timestamp <= $cachetime && $wgCacheEpoch <= $cachetime );
00126         wfDebug( __METHOD__ .
00127             ": cachetime $cachetime, touched '{$timestamp}' epoch {$wgCacheEpoch}, good $good\n" );
00128 
00129         return $good;
00130     }
00131 
00136     protected function useGzip() {
00137         return $this->mUseGzip;
00138     }
00139 
00144     public function fetchText() {
00145         if ( $this->useGzip() ) {
00146             $fh = gzopen( $this->cachePath(), 'rb' );
00147 
00148             return stream_get_contents( $fh );
00149         } else {
00150             return file_get_contents( $this->cachePath() );
00151         }
00152     }
00153 
00159     public function saveText( $text ) {
00160         global $wgUseFileCache;
00161 
00162         if ( !$wgUseFileCache ) {
00163             return false;
00164         }
00165 
00166         if ( $this->useGzip() ) {
00167             $text = gzencode( $text );
00168         }
00169 
00170         $this->checkCacheDirs(); // build parent dir
00171         if ( !file_put_contents( $this->cachePath(), $text, LOCK_EX ) ) {
00172             wfDebug( __METHOD__ . "() failed saving " . $this->cachePath() . "\n" );
00173             $this->mCached = null;
00174 
00175             return false;
00176         }
00177 
00178         $this->mCached = true;
00179 
00180         return $text;
00181     }
00182 
00187     public function clearCache() {
00188         wfSuppressWarnings();
00189         unlink( $this->cachePath() );
00190         wfRestoreWarnings();
00191         $this->mCached = false;
00192     }
00193 
00198     protected function checkCacheDirs() {
00199         wfMkdirParents( dirname( $this->cachePath() ), null, __METHOD__ );
00200     }
00201 
00209     protected function typeSubdirectory() {
00210         return $this->mType . '/';
00211     }
00212 
00218     protected function hashSubdirectory() {
00219         global $wgFileCacheDepth;
00220 
00221         $subdir = '';
00222         if ( $wgFileCacheDepth > 0 ) {
00223             $hash = md5( $this->mKey );
00224             for ( $i = 1; $i <= $wgFileCacheDepth; $i++ ) {
00225                 $subdir .= substr( $hash, 0, $i ) . '/';
00226             }
00227         }
00228 
00229         return $subdir;
00230     }
00231 
00237     public function incrMissesRecent( WebRequest $request ) {
00238         global $wgMemc;
00239         if ( mt_rand( 0, self::MISS_FACTOR - 1 ) == 0 ) {
00240             # Get a large IP range that should include the user  even if that
00241             # person's IP address changes
00242             $ip = $request->getIP();
00243             if ( !IP::isValid( $ip ) ) {
00244                 return;
00245             }
00246             $ip = IP::isIPv6( $ip )
00247                 ? IP::sanitizeRange( "$ip/32" )
00248                 : IP::sanitizeRange( "$ip/16" );
00249 
00250             # Bail out if a request already came from this range...
00251             $key = wfMemcKey( get_class( $this ), 'attempt', $this->mType, $this->mKey, $ip );
00252             if ( $wgMemc->get( $key ) ) {
00253                 return; // possibly the same user
00254             }
00255             $wgMemc->set( $key, 1, self::MISS_TTL_SEC );
00256 
00257             # Increment the number of cache misses...
00258             $key = $this->cacheMissKey();
00259             if ( $wgMemc->get( $key ) === false ) {
00260                 $wgMemc->set( $key, 1, self::MISS_TTL_SEC );
00261             } else {
00262                 $wgMemc->incr( $key );
00263             }
00264         }
00265     }
00266 
00271     public function getMissesRecent() {
00272         global $wgMemc;
00273 
00274         return self::MISS_FACTOR * $wgMemc->get( $this->cacheMissKey() );
00275     }
00276 
00280     protected function cacheMissKey() {
00281         return wfMemcKey( get_class( $this ), 'misses', $this->mType, $this->mKey );
00282     }
00283 }