MediaWiki  REL1_19
ParserCache.php
Go to the documentation of this file.
00001 <?php
00012 class ParserCache {
00013         private $mMemc;
00014         const try116cache = false; /* Only useful $wgParserCacheExpireTime after updating to 1.17 */
00015 
00021         public static function singleton() {
00022                 static $instance;
00023                 if ( !isset( $instance ) ) {
00024                         global $parserMemc;
00025                         $instance = new ParserCache( $parserMemc );
00026                 }
00027                 return $instance;
00028         }
00029 
00036         protected function __construct( $memCached ) {
00037                 if ( !$memCached ) {
00038                         throw new MWException( "Tried to create a ParserCache with an invalid memcached" );
00039                 }
00040                 $this->mMemc = $memCached;
00041         }
00042 
00048         protected function getParserOutputKey( $article, $hash ) {
00049                 global $wgRequest;
00050 
00051                 // idhash seem to mean 'page id' + 'rendering hash' (r3710)
00052                 $pageid = $article->getID();
00053                 $renderkey = (int)($wgRequest->getVal('action') == 'render');
00054 
00055                 $key = wfMemcKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" );
00056                 return $key;
00057         }
00058 
00063         protected function getOptionsKey( $article ) {
00064                 $pageid = $article->getID();
00065                 return wfMemcKey( 'pcache', 'idoptions', "{$pageid}" );
00066         }
00067 
00081         function getETag( $article, $popts ) {
00082                 return 'W/"' . $this->getParserOutputKey( $article,
00083                         $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) ) .
00084                                 "--" . $article->getTouched() . '"';
00085         }
00086 
00093         public function getDirty( $article, $popts ) {
00094                 $value = $this->get( $article, $popts, true );
00095                 return is_object( $value ) ? $value : false;
00096         }
00097 
00108         public function getKey( $article, $popts, $useOutdated = true ) {
00109                 global $wgCacheEpoch;
00110 
00111                 if( $popts instanceof User ) {
00112                         wfWarn( "Use of outdated prototype ParserCache::getKey( &\$article, &\$user )\n" );
00113                         $popts = ParserOptions::newFromUser( $popts );
00114                 }
00115 
00116                 // Determine the options which affect this article
00117                 $optionsKey = $this->mMemc->get( $this->getOptionsKey( $article ) );
00118                 if ( $optionsKey != false ) {
00119                         if ( !$useOutdated && $optionsKey->expired( $article->getTouched() ) ) {
00120                                 wfIncrStats( "pcache_miss_expired" );
00121                                 $cacheTime = $optionsKey->getCacheTime();
00122                                 wfDebug( "Parser options key expired, touched " . $article->getTouched() . ", epoch $wgCacheEpoch, cached $cacheTime\n" );
00123                                 return false;
00124                         }
00125 
00126                         $usedOptions = $optionsKey->mUsedOptions;
00127                         wfDebug( "Parser cache options found.\n" );
00128                 } else {
00129                         if ( !$useOutdated && !self::try116cache ) {
00130                                 return false;
00131                         }
00132                         $usedOptions = ParserOptions::legacyOptions();
00133                 }
00134 
00135                 return $this->getParserOutputKey( $article, $popts->optionsHash( $usedOptions, $article->getTitle() ) );
00136         }
00137 
00148         public function get( $article, $popts, $useOutdated = false ) {
00149                 global $wgCacheEpoch;
00150                 wfProfileIn( __METHOD__ );
00151 
00152                 $canCache = $article->checkTouched();
00153                 if ( !$canCache ) {
00154                         // It's a redirect now
00155                         wfProfileOut( __METHOD__ );
00156                         return false;
00157                 }
00158 
00159                 $touched = $article->getTouched();
00160 
00161                 $parserOutputKey = $this->getKey( $article, $popts, $useOutdated );
00162                 if ( $parserOutputKey === false ) {
00163                         wfIncrStats( 'pcache_miss_absent' );
00164                         wfProfileOut( __METHOD__ );
00165                         return false;
00166                 }
00167 
00168                 $value = $this->mMemc->get( $parserOutputKey );
00169                 if ( self::try116cache && !$value && strpos( $value, '*' ) !== -1 ) {
00170                         wfDebug( "New format parser cache miss.\n" );
00171                         $parserOutputKey = $this->getParserOutputKey( $article,
00172                                 $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) );
00173                         $value = $this->mMemc->get( $parserOutputKey );
00174                 }
00175                 if ( !$value ) {
00176                         wfDebug( "ParserOutput cache miss.\n" );
00177                         wfIncrStats( "pcache_miss_absent" );
00178                         wfProfileOut( __METHOD__ );
00179                         return false;
00180                 }
00181 
00182                 wfDebug( "ParserOutput cache found.\n" );
00183 
00184                 // The edit section preference may not be the appropiate one in 
00185                 // the ParserOutput, as we are not storing it in the parsercache 
00186                 // key. Force it here. See bug 31445.
00187                 $value->setEditSectionTokens( $popts->getEditSection() );
00188 
00189                 if ( !$useOutdated && $value->expired( $touched ) ) {
00190                         wfIncrStats( "pcache_miss_expired" );
00191                         $cacheTime = $value->getCacheTime();
00192                         wfDebug( "ParserOutput key expired, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" );
00193                         $value = false;
00194                 } else {
00195                         wfIncrStats( "pcache_hit" );
00196                 }
00197 
00198                 wfProfileOut( __METHOD__ );
00199                 return $value;
00200         }
00201 
00207         public function save( $parserOutput, $article, $popts ) {
00208                 $expire = $parserOutput->getCacheExpiry();
00209 
00210                 if( $expire > 0 ) {
00211                         $now = wfTimestampNow();
00212 
00213                         $optionsKey = new CacheTime;
00214                         $optionsKey->mUsedOptions = $parserOutput->getUsedOptions();
00215                         $optionsKey->updateCacheExpiry( $expire );
00216 
00217                         $optionsKey->setCacheTime( $now );
00218                         $parserOutput->setCacheTime( $now );
00219 
00220                         $optionsKey->setContainsOldMagic( $parserOutput->containsOldMagic() );
00221 
00222                         $parserOutputKey = $this->getParserOutputKey( $article,
00223                                 $popts->optionsHash( $optionsKey->mUsedOptions, $article->getTitle() ) );
00224 
00225                         // Save the timestamp so that we don't have to load the revision row on view
00226                         $parserOutput->setTimestamp( $article->getTimestamp() );
00227 
00228                         $parserOutput->mText .= "\n<!-- Saved in parser cache with key $parserOutputKey and timestamp $now -->\n";
00229                         wfDebug( "Saved in parser cache with key $parserOutputKey and timestamp $now\n" );
00230 
00231                         // Save the parser output
00232                         $this->mMemc->set( $parserOutputKey, $parserOutput, $expire );
00233 
00234                         // ...and its pointer
00235                         $this->mMemc->set( $this->getOptionsKey( $article ), $optionsKey, $expire );
00236                 } else {
00237                         wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
00238                 }
00239         }
00240 }