MediaWiki
REL1_22
|
00001 <?php 00028 class ParserCache { 00029 private $mMemc; 00030 const try116cache = false; /* Only useful $wgParserCacheExpireTime after updating to 1.17 */ 00031 00037 public static function singleton() { 00038 static $instance; 00039 if ( !isset( $instance ) ) { 00040 global $parserMemc; 00041 $instance = new ParserCache( $parserMemc ); 00042 } 00043 return $instance; 00044 } 00045 00053 protected function __construct( $memCached ) { 00054 if ( !$memCached ) { 00055 throw new MWException( "Tried to create a ParserCache with an invalid memcached" ); 00056 } 00057 $this->mMemc = $memCached; 00058 } 00059 00065 protected function getParserOutputKey( $article, $hash ) { 00066 global $wgRequest; 00067 00068 // idhash seem to mean 'page id' + 'rendering hash' (r3710) 00069 $pageid = $article->getID(); 00070 $renderkey = (int)( $wgRequest->getVal( 'action' ) == 'render' ); 00071 00072 $key = wfMemcKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" ); 00073 return $key; 00074 } 00075 00080 protected function getOptionsKey( $article ) { 00081 $pageid = $article->getID(); 00082 return wfMemcKey( 'pcache', 'idoptions', "{$pageid}" ); 00083 } 00084 00099 function getETag( $article, $popts ) { 00100 return 'W/"' . $this->getParserOutputKey( $article, 00101 $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) ) . 00102 "--" . $article->getTouched() . '"'; 00103 } 00104 00111 public function getDirty( $article, $popts ) { 00112 $value = $this->get( $article, $popts, true ); 00113 return is_object( $value ) ? $value : false; 00114 } 00115 00128 public function getKey( $article, $popts, $useOutdated = true ) { 00129 global $wgCacheEpoch; 00130 00131 if ( $popts instanceof User ) { 00132 wfWarn( "Use of outdated prototype ParserCache::getKey( &\$article, &\$user )\n" ); 00133 $popts = ParserOptions::newFromUser( $popts ); 00134 } 00135 00136 // Determine the options which affect this article 00137 $optionsKey = $this->mMemc->get( $this->getOptionsKey( $article ) ); 00138 if ( $optionsKey != false ) { 00139 if ( !$useOutdated && $optionsKey->expired( $article->getTouched() ) ) { 00140 wfIncrStats( "pcache_miss_expired" ); 00141 $cacheTime = $optionsKey->getCacheTime(); 00142 wfDebug( "Parser options key expired, touched " . $article->getTouched() . ", epoch $wgCacheEpoch, cached $cacheTime\n" ); 00143 return false; 00144 } 00145 00146 $usedOptions = $optionsKey->mUsedOptions; 00147 wfDebug( "Parser cache options found.\n" ); 00148 } else { 00149 if ( !$useOutdated && !self::try116cache ) { 00150 return false; 00151 } 00152 $usedOptions = ParserOptions::legacyOptions(); 00153 } 00154 00155 return $this->getParserOutputKey( $article, $popts->optionsHash( $usedOptions, $article->getTitle() ) ); 00156 } 00157 00168 public function get( $article, $popts, $useOutdated = false ) { 00169 global $wgCacheEpoch; 00170 wfProfileIn( __METHOD__ ); 00171 00172 $canCache = $article->checkTouched(); 00173 if ( !$canCache ) { 00174 // It's a redirect now 00175 wfProfileOut( __METHOD__ ); 00176 return false; 00177 } 00178 00179 $touched = $article->getTouched(); 00180 00181 $parserOutputKey = $this->getKey( $article, $popts, $useOutdated ); 00182 if ( $parserOutputKey === false ) { 00183 wfIncrStats( 'pcache_miss_absent' ); 00184 wfProfileOut( __METHOD__ ); 00185 return false; 00186 } 00187 00188 $value = $this->mMemc->get( $parserOutputKey ); 00189 if ( self::try116cache && !$value && strpos( $value, '*' ) !== -1 ) { 00190 wfDebug( "New format parser cache miss.\n" ); 00191 $parserOutputKey = $this->getParserOutputKey( $article, 00192 $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) ); 00193 $value = $this->mMemc->get( $parserOutputKey ); 00194 } 00195 if ( !$value ) { 00196 wfDebug( "ParserOutput cache miss.\n" ); 00197 wfIncrStats( "pcache_miss_absent" ); 00198 wfProfileOut( __METHOD__ ); 00199 return false; 00200 } 00201 00202 wfDebug( "ParserOutput cache found.\n" ); 00203 00204 // The edit section preference may not be the appropiate one in 00205 // the ParserOutput, as we are not storing it in the parsercache 00206 // key. Force it here. See bug 31445. 00207 $value->setEditSectionTokens( $popts->getEditSection() ); 00208 00209 if ( !$useOutdated && $value->expired( $touched ) ) { 00210 wfIncrStats( "pcache_miss_expired" ); 00211 $cacheTime = $value->getCacheTime(); 00212 wfDebug( "ParserOutput key expired, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" ); 00213 $value = false; 00214 } else { 00215 wfIncrStats( "pcache_hit" ); 00216 } 00217 00218 wfProfileOut( __METHOD__ ); 00219 return $value; 00220 } 00221 00228 public function save( $parserOutput, $article, $popts, $cacheTime = null ) { 00229 $expire = $parserOutput->getCacheExpiry(); 00230 if ( $expire > 0 ) { 00231 $cacheTime = $cacheTime ?: wfTimestampNow(); 00232 00233 $optionsKey = new CacheTime; 00234 $optionsKey->mUsedOptions = $parserOutput->getUsedOptions(); 00235 $optionsKey->updateCacheExpiry( $expire ); 00236 00237 $optionsKey->setCacheTime( $cacheTime ); 00238 $parserOutput->setCacheTime( $cacheTime ); 00239 00240 $optionsKey->setContainsOldMagic( $parserOutput->containsOldMagic() ); 00241 00242 $parserOutputKey = $this->getParserOutputKey( $article, 00243 $popts->optionsHash( $optionsKey->mUsedOptions, $article->getTitle() ) ); 00244 00245 // Save the timestamp so that we don't have to load the revision row on view 00246 $parserOutput->setTimestamp( $article->getTimestamp() ); 00247 00248 $parserOutput->mText .= "\n<!-- Saved in parser cache with key $parserOutputKey and timestamp $cacheTime\n -->\n"; 00249 wfDebug( "Saved in parser cache with key $parserOutputKey and timestamp $cacheTime\n" ); 00250 00251 // Save the parser output 00252 $this->mMemc->set( $parserOutputKey, $parserOutput, $expire ); 00253 00254 // ...and its pointer 00255 $this->mMemc->set( $this->getOptionsKey( $article ), $optionsKey, $expire ); 00256 } else { 00257 wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" ); 00258 } 00259 } 00260 }