MediaWiki
REL1_19
|
00001 <?php 00007 abstract class FileCacheBase { 00008 protected $mKey; 00009 protected $mType = 'object'; 00010 protected $mExt = 'cache'; 00011 protected $mFilePath; 00012 protected $mUseGzip; 00013 /* lazy loaded */ 00014 protected $mCached; 00015 00016 /* @TODO: configurable? */ 00017 const MISS_FACTOR = 15; // log 1 every MISS_FACTOR cache misses 00018 const MISS_TTL_SEC = 3600; // how many seconds ago is "recent" 00019 00020 protected function __construct() { 00021 global $wgUseGzip; 00022 00023 $this->mUseGzip = (bool)$wgUseGzip; 00024 } 00025 00030 final protected function baseCacheDirectory() { 00031 global $wgFileCacheDirectory; 00032 return $wgFileCacheDirectory; 00033 } 00034 00039 abstract protected function cacheDirectory(); 00040 00045 protected function cachePath() { 00046 if ( $this->mFilePath !== null ) { 00047 return $this->mFilePath; 00048 } 00049 00050 $dir = $this->cacheDirectory(); 00051 # Build directories (methods include the trailing "/") 00052 $subDirs = $this->typeSubdirectory() . $this->hashSubdirectory(); 00053 # Avoid extension confusion 00054 $key = str_replace( '.', '%2E', urlencode( $this->mKey ) ); 00055 # Build the full file path 00056 $this->mFilePath = "{$dir}/{$subDirs}{$key}.{$this->mExt}"; 00057 if ( $this->useGzip() ) { 00058 $this->mFilePath .= '.gz'; 00059 } 00060 00061 return $this->mFilePath; 00062 } 00063 00068 public function isCached() { 00069 if ( $this->mCached === null ) { 00070 $this->mCached = file_exists( $this->cachePath() ); 00071 } 00072 return $this->mCached; 00073 } 00074 00079 public function cacheTimestamp() { 00080 $timestamp = filemtime( $this->cachePath() ); 00081 return ( $timestamp !== false ) 00082 ? wfTimestamp( TS_MW, $timestamp ) 00083 : false; 00084 } 00085 00092 public function isCacheGood( $timestamp = '' ) { 00093 global $wgCacheEpoch; 00094 00095 if ( !$this->isCached() ) { 00096 return false; 00097 } 00098 00099 $cachetime = $this->cacheTimestamp(); 00100 $good = ( $timestamp <= $cachetime && $wgCacheEpoch <= $cachetime ); 00101 wfDebug( __METHOD__ . ": cachetime $cachetime, touched '{$timestamp}' epoch {$wgCacheEpoch}, good $good\n" ); 00102 00103 return $good; 00104 } 00105 00110 protected function useGzip() { 00111 return $this->mUseGzip; 00112 } 00113 00118 public function fetchText() { 00119 if( $this->useGzip() ) { 00120 $fh = gzopen( $this->cachePath(), 'rb' ); 00121 return stream_get_contents( $fh ); 00122 } else { 00123 return file_get_contents( $this->cachePath() ); 00124 } 00125 } 00126 00131 public function saveText( $text ) { 00132 global $wgUseFileCache; 00133 00134 if ( !$wgUseFileCache ) { 00135 return false; 00136 } 00137 00138 if ( $this->useGzip() ) { 00139 $text = gzencode( $text ); 00140 } 00141 00142 $this->checkCacheDirs(); // build parent dir 00143 if ( !file_put_contents( $this->cachePath(), $text, LOCK_EX ) ) { 00144 wfDebug( __METHOD__ . "() failed saving ". $this->cachePath() . "\n"); 00145 $this->mCached = null; 00146 return false; 00147 } 00148 00149 $this->mCached = true; 00150 return $text; 00151 } 00152 00157 public function clearCache() { 00158 wfSuppressWarnings(); 00159 unlink( $this->cachePath() ); 00160 wfRestoreWarnings(); 00161 $this->mCached = false; 00162 } 00163 00168 protected function checkCacheDirs() { 00169 wfMkdirParents( dirname( $this->cachePath() ), null, __METHOD__ ); 00170 } 00171 00179 protected function typeSubdirectory() { 00180 return $this->mType . '/'; 00181 } 00182 00188 protected function hashSubdirectory() { 00189 global $wgFileCacheDepth; 00190 00191 $subdir = ''; 00192 if ( $wgFileCacheDepth > 0 ) { 00193 $hash = md5( $this->mKey ); 00194 for ( $i = 1; $i <= $wgFileCacheDepth; $i++ ) { 00195 $subdir .= substr( $hash, 0, $i ) . '/'; 00196 } 00197 } 00198 00199 return $subdir; 00200 } 00201 00207 public function incrMissesRecent( WebRequest $request ) { 00208 global $wgMemc; 00209 if ( mt_rand( 0, self::MISS_FACTOR - 1 ) == 0 ) { 00210 # Get a large IP range that should include the user even if that 00211 # person's IP address changes 00212 $ip = $request->getIP(); 00213 if ( !IP::isValid( $ip ) ) { 00214 return; 00215 } 00216 $ip = IP::isIPv6( $ip ) 00217 ? IP::sanitizeRange( "$ip/32" ) 00218 : IP::sanitizeRange( "$ip/16" ); 00219 00220 # Bail out if a request already came from this range... 00221 $key = wfMemcKey( get_class( $this ), 'attempt', $this->mType, $this->mKey, $ip ); 00222 if ( $wgMemc->get( $key ) ) { 00223 return; // possibly the same user 00224 } 00225 $wgMemc->set( $key, 1, self::MISS_TTL_SEC ); 00226 00227 # Increment the number of cache misses... 00228 $key = $this->cacheMissKey(); 00229 if ( $wgMemc->get( $key ) === false ) { 00230 $wgMemc->set( $key, 1, self::MISS_TTL_SEC ); 00231 } else { 00232 $wgMemc->incr( $key ); 00233 } 00234 } 00235 } 00236 00241 public function getMissesRecent() { 00242 global $wgMemc; 00243 return self::MISS_FACTOR * $wgMemc->get( $this->cacheMissKey() ); 00244 } 00245 00249 protected function cacheMissKey() { 00250 return wfMemcKey( get_class( $this ), 'misses', $this->mType, $this->mKey ); 00251 } 00252 }