MediaWiki  REL1_20
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 
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         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 
00127         public function getKey( $article, $popts, $useOutdated = true ) {
00128                 global $wgCacheEpoch;
00129 
00130                 if( $popts instanceof User ) {
00131                         wfWarn( "Use of outdated prototype ParserCache::getKey( &\$article, &\$user )\n" );
00132                         $popts = ParserOptions::newFromUser( $popts );
00133                 }
00134 
00135                 // Determine the options which affect this article
00136                 $optionsKey = $this->mMemc->get( $this->getOptionsKey( $article ) );
00137                 if ( $optionsKey != false ) {
00138                         if ( !$useOutdated && $optionsKey->expired( $article->getTouched() ) ) {
00139                                 wfIncrStats( "pcache_miss_expired" );
00140                                 $cacheTime = $optionsKey->getCacheTime();
00141                                 wfDebug( "Parser options key expired, touched " . $article->getTouched() . ", epoch $wgCacheEpoch, cached $cacheTime\n" );
00142                                 return false;
00143                         }
00144 
00145                         $usedOptions = $optionsKey->mUsedOptions;
00146                         wfDebug( "Parser cache options found.\n" );
00147                 } else {
00148                         if ( !$useOutdated && !self::try116cache ) {
00149                                 return false;
00150                         }
00151                         $usedOptions = ParserOptions::legacyOptions();
00152                 }
00153 
00154                 return $this->getParserOutputKey( $article, $popts->optionsHash( $usedOptions, $article->getTitle() ) );
00155         }
00156 
00167         public function get( $article, $popts, $useOutdated = false ) {
00168                 global $wgCacheEpoch;
00169                 wfProfileIn( __METHOD__ );
00170 
00171                 $canCache = $article->checkTouched();
00172                 if ( !$canCache ) {
00173                         // It's a redirect now
00174                         wfProfileOut( __METHOD__ );
00175                         return false;
00176                 }
00177 
00178                 $touched = $article->getTouched();
00179 
00180                 $parserOutputKey = $this->getKey( $article, $popts, $useOutdated );
00181                 if ( $parserOutputKey === false ) {
00182                         wfIncrStats( 'pcache_miss_absent' );
00183                         wfProfileOut( __METHOD__ );
00184                         return false;
00185                 }
00186 
00187                 $value = $this->mMemc->get( $parserOutputKey );
00188                 if ( self::try116cache && !$value && strpos( $value, '*' ) !== -1 ) {
00189                         wfDebug( "New format parser cache miss.\n" );
00190                         $parserOutputKey = $this->getParserOutputKey( $article,
00191                                 $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) );
00192                         $value = $this->mMemc->get( $parserOutputKey );
00193                 }
00194                 if ( !$value ) {
00195                         wfDebug( "ParserOutput cache miss.\n" );
00196                         wfIncrStats( "pcache_miss_absent" );
00197                         wfProfileOut( __METHOD__ );
00198                         return false;
00199                 }
00200 
00201                 wfDebug( "ParserOutput cache found.\n" );
00202 
00203                 // The edit section preference may not be the appropiate one in 
00204                 // the ParserOutput, as we are not storing it in the parsercache 
00205                 // key. Force it here. See bug 31445.
00206                 $value->setEditSectionTokens( $popts->getEditSection() );
00207 
00208                 if ( !$useOutdated && $value->expired( $touched ) ) {
00209                         wfIncrStats( "pcache_miss_expired" );
00210                         $cacheTime = $value->getCacheTime();
00211                         wfDebug( "ParserOutput key expired, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" );
00212                         $value = false;
00213                 } else {
00214                         wfIncrStats( "pcache_hit" );
00215                 }
00216 
00217                 wfProfileOut( __METHOD__ );
00218                 return $value;
00219         }
00220 
00226         public function save( $parserOutput, $article, $popts ) {
00227                 $expire = $parserOutput->getCacheExpiry();
00228 
00229                 if( $expire > 0 ) {
00230                         $now = wfTimestampNow();
00231 
00232                         $optionsKey = new CacheTime;
00233                         $optionsKey->mUsedOptions = $parserOutput->getUsedOptions();
00234                         $optionsKey->updateCacheExpiry( $expire );
00235 
00236                         $optionsKey->setCacheTime( $now );
00237                         $parserOutput->setCacheTime( $now );
00238 
00239                         $optionsKey->setContainsOldMagic( $parserOutput->containsOldMagic() );
00240 
00241                         $parserOutputKey = $this->getParserOutputKey( $article,
00242                                 $popts->optionsHash( $optionsKey->mUsedOptions, $article->getTitle() ) );
00243 
00244                         // Save the timestamp so that we don't have to load the revision row on view
00245                         $parserOutput->setTimestamp( $article->getTimestamp() );
00246 
00247                         $parserOutput->mText .= "\n<!-- Saved in parser cache with key $parserOutputKey and timestamp $now -->\n";
00248                         wfDebug( "Saved in parser cache with key $parserOutputKey and timestamp $now\n" );
00249 
00250                         // Save the parser output
00251                         $this->mMemc->set( $parserOutputKey, $parserOutput, $expire );
00252 
00253                         // ...and its pointer
00254                         $this->mMemc->set( $this->getOptionsKey( $article ), $optionsKey, $expire );
00255                 } else {
00256                         wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
00257                 }
00258         }
00259 }