MediaWiki  REL1_24
ParserCache.php
Go to the documentation of this file.
00001 <?php
00028 class ParserCache {
00030     private $mMemc;
00036     public static function singleton() {
00037         static $instance;
00038         if ( !isset( $instance ) ) {
00039             global $parserMemc;
00040             $instance = new ParserCache( $parserMemc );
00041         }
00042         return $instance;
00043     }
00044 
00052     protected function __construct( $memCached ) {
00053         if ( !$memCached ) {
00054             throw new MWException( "Tried to create a ParserCache with an invalid memcached" );
00055         }
00056         $this->mMemc = $memCached;
00057     }
00058 
00064     protected function getParserOutputKey( $article, $hash ) {
00065         global $wgRequest;
00066 
00067         // idhash seem to mean 'page id' + 'rendering hash' (r3710)
00068         $pageid = $article->getID();
00069         $renderkey = (int)( $wgRequest->getVal( 'action' ) == 'render' );
00070 
00071         $key = wfMemcKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" );
00072         return $key;
00073     }
00074 
00079     protected function getOptionsKey( $article ) {
00080         $pageid = $article->getID();
00081         return wfMemcKey( 'pcache', 'idoptions', "{$pageid}" );
00082     }
00083 
00098     public function getETag( $article, $popts ) {
00099         return 'W/"' . $this->getParserOutputKey( $article,
00100             $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) ) .
00101                 "--" . $article->getTouched() . '"';
00102     }
00103 
00110     public function getDirty( $article, $popts ) {
00111         $value = $this->get( $article, $popts, true );
00112         return is_object( $value ) ? $value : false;
00113     }
00114 
00134     public function getKey( $article, $popts, $useOutdated = true ) {
00135         global $wgCacheEpoch;
00136 
00137         if ( $popts instanceof User ) {
00138             wfWarn( "Use of outdated prototype ParserCache::getKey( &\$article, &\$user )\n" );
00139             $popts = ParserOptions::newFromUser( $popts );
00140         }
00141 
00142         // Determine the options which affect this article
00143         $optionsKey = $this->mMemc->get( $this->getOptionsKey( $article ) );
00144         if ( $optionsKey != false ) {
00145             if ( !$useOutdated && $optionsKey->expired( $article->getTouched() ) ) {
00146                 wfIncrStats( "pcache_miss_expired" );
00147                 $cacheTime = $optionsKey->getCacheTime();
00148                 wfDebug( "Parser options key expired, touched " . $article->getTouched()
00149                     . ", epoch $wgCacheEpoch, cached $cacheTime\n" );
00150                 return false;
00151             } elseif ( $optionsKey->isDifferentRevision( $article->getLatest() ) ) {
00152                 wfIncrStats( "pcache_miss_revid" );
00153                 $revId = $article->getLatest();
00154                 $cachedRevId = $optionsKey->getCacheRevisionId();
00155                 wfDebug( "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n" );
00156                 return false;
00157             }
00158 
00159             // $optionsKey->mUsedOptions is set by save() by calling ParserOutput::getUsedOptions()
00160             $usedOptions = $optionsKey->mUsedOptions;
00161             wfDebug( "Parser cache options found.\n" );
00162         } else {
00163             if ( !$useOutdated ) {
00164                 return false;
00165             }
00166             $usedOptions = ParserOptions::legacyOptions();
00167         }
00168 
00169         return $this->getParserOutputKey(
00170             $article,
00171             $popts->optionsHash( $usedOptions, $article->getTitle() )
00172         );
00173     }
00174 
00185     public function get( $article, $popts, $useOutdated = false ) {
00186         global $wgCacheEpoch;
00187         wfProfileIn( __METHOD__ );
00188 
00189         $canCache = $article->checkTouched();
00190         if ( !$canCache ) {
00191             // It's a redirect now
00192             wfProfileOut( __METHOD__ );
00193             return false;
00194         }
00195 
00196         $touched = $article->getTouched();
00197 
00198         $parserOutputKey = $this->getKey( $article, $popts, $useOutdated );
00199         if ( $parserOutputKey === false ) {
00200             wfIncrStats( 'pcache_miss_absent' );
00201             wfProfileOut( __METHOD__ );
00202             return false;
00203         }
00204 
00205         $value = $this->mMemc->get( $parserOutputKey );
00206         if ( !$value ) {
00207             wfDebug( "ParserOutput cache miss.\n" );
00208             wfIncrStats( "pcache_miss_absent" );
00209             wfProfileOut( __METHOD__ );
00210             return false;
00211         }
00212 
00213         wfDebug( "ParserOutput cache found.\n" );
00214 
00215         // The edit section preference may not be the appropiate one in
00216         // the ParserOutput, as we are not storing it in the parsercache
00217         // key. Force it here. See bug 31445.
00218         $value->setEditSectionTokens( $popts->getEditSection() );
00219 
00220         if ( !$useOutdated && $value->expired( $touched ) ) {
00221             wfIncrStats( "pcache_miss_expired" );
00222             $cacheTime = $value->getCacheTime();
00223             wfDebug( "ParserOutput key expired, touched $touched, "
00224                 . "epoch $wgCacheEpoch, cached $cacheTime\n" );
00225             $value = false;
00226         } elseif ( $value->isDifferentRevision( $article->getLatest() ) ) {
00227             wfIncrStats( "pcache_miss_revid" );
00228             $revId = $article->getLatest();
00229             $cachedRevId = $value->getCacheRevisionId();
00230             wfDebug( "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n" );
00231             $value = false;
00232         } else {
00233             wfIncrStats( "pcache_hit" );
00234         }
00235 
00236         wfProfileOut( __METHOD__ );
00237         return $value;
00238     }
00239 
00247     public function save( $parserOutput, $page, $popts, $cacheTime = null, $revId = null ) {
00248         $expire = $parserOutput->getCacheExpiry();
00249         if ( $expire > 0 ) {
00250             $cacheTime = $cacheTime ?: wfTimestampNow();
00251             if ( !$revId ) {
00252                 $revision = $page->getRevision();
00253                 $revId = $revision ? $revision->getId() : null;
00254             }
00255 
00256             $optionsKey = new CacheTime;
00257             $optionsKey->mUsedOptions = $parserOutput->getUsedOptions();
00258             $optionsKey->updateCacheExpiry( $expire );
00259 
00260             $optionsKey->setCacheTime( $cacheTime );
00261             $parserOutput->setCacheTime( $cacheTime );
00262             $optionsKey->setCacheRevisionId( $revId );
00263             $parserOutput->setCacheRevisionId( $revId );
00264 
00265             $optionsKey->setContainsOldMagic( $parserOutput->containsOldMagic() );
00266 
00267             $parserOutputKey = $this->getParserOutputKey( $page,
00268                 $popts->optionsHash( $optionsKey->mUsedOptions, $page->getTitle() ) );
00269 
00270             // Save the timestamp so that we don't have to load the revision row on view
00271             $parserOutput->setTimestamp( $page->getTimestamp() );
00272 
00273             $msg = "Saved in parser cache with key $parserOutputKey" .
00274                 " and timestamp $cacheTime" .
00275                 " and revision id $revId" .
00276                 "\n";
00277 
00278             $parserOutput->mText .= "\n<!-- $msg -->\n";
00279             wfDebug( $msg );
00280 
00281             // Save the parser output
00282             $this->mMemc->set( $parserOutputKey, $parserOutput, $expire );
00283 
00284             // ...and its pointer
00285             $this->mMemc->set( $this->getOptionsKey( $page ), $optionsKey, $expire );
00286         } else {
00287             wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
00288         }
00289     }
00290 }