MediaWiki  REL1_21
ParserCache.php
Go to the documentation of this file.
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 
00227         public function save( $parserOutput, $article, $popts ) {
00228                 $expire = $parserOutput->getCacheExpiry();
00229 
00230                 if( $expire > 0 ) {
00231                         $now = wfTimestampNow();
00232 
00233                         $optionsKey = new CacheTime;
00234                         $optionsKey->mUsedOptions = $parserOutput->getUsedOptions();
00235                         $optionsKey->updateCacheExpiry( $expire );
00236 
00237                         $optionsKey->setCacheTime( $now );
00238                         $parserOutput->setCacheTime( $now );
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 $now -->\n";
00249                         wfDebug( "Saved in parser cache with key $parserOutputKey and timestamp $now\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 }