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