MediaWiki
REL1_22
|
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 }