MediaWiki  master
ParserCache.php
Go to the documentation of this file.
1 <?php
25 
30 class ParserCache {
37  const USE_CURRENT_ONLY = 0;
38 
40  const USE_EXPIRED = 1;
41 
43  const USE_OUTDATED = 2;
44 
49  const USE_ANYTHING = 3;
50 
52  private $cache;
53 
59  private $cacheEpoch;
60 
67  public static function singleton() {
68  return MediaWikiServices::getInstance()->getParserCache();
69  }
70 
81  public function __construct( BagOStuff $cache, $cacheEpoch = '20030516000000' ) {
82  $this->cache = $cache;
83  $this->cacheEpoch = $cacheEpoch;
84  }
85 
91  protected function getParserOutputKey( $article, $hash ) {
92  global $wgRequest;
93 
94  // idhash seem to mean 'page id' + 'rendering hash' (r3710)
95  $pageid = $article->getId();
96  $renderkey = (int)( $wgRequest->getVal( 'action' ) == 'render' );
97 
98  $key = $this->cache->makeKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" );
99  return $key;
100  }
101 
106  protected function getOptionsKey( $page ) {
107  return $this->cache->makeKey( 'pcache', 'idoptions', $page->getId() );
108  }
109 
114  public function deleteOptionsKey( $page ) {
115  $this->cache->delete( $this->getOptionsKey( $page ) );
116  }
117 
132  public function getETag( $article, $popts ) {
133  return 'W/"' . $this->getParserOutputKey( $article,
134  $popts->optionsHash( ParserOptions::allCacheVaryingOptions(), $article->getTitle() ) ) .
135  "--" . $article->getTouched() . '"';
136  }
137 
144  public function getDirty( $article, $popts ) {
145  $value = $this->get( $article, $popts, true );
146  return is_object( $value ) ? $value : false;
147  }
148 
153  private function incrementStats( $article, $metricSuffix ) {
154  $contentModel = str_replace( '.', '_', $article->getContentModel() );
155  $metricSuffix = str_replace( '.', '_', $metricSuffix );
156  wfIncrStats( 'pcache.' . $contentModel . '.' . $metricSuffix );
157  }
158 
179  public function getKey( $article, $popts, $useOutdated = self::USE_ANYTHING ) {
180  if ( is_bool( $useOutdated ) ) {
181  $useOutdated = $useOutdated ? self::USE_ANYTHING : self::USE_CURRENT_ONLY;
182  }
183 
184  if ( $popts instanceof User ) {
185  wfWarn( "Use of outdated prototype ParserCache::getKey( &\$article, &\$user )\n" );
186  $popts = ParserOptions::newFromUser( $popts );
187  }
188 
189  // Determine the options which affect this article
190  $optionsKey = $this->cache->get(
191  $this->getOptionsKey( $article ), BagOStuff::READ_VERIFIED );
192  if ( $optionsKey instanceof CacheTime ) {
193  if ( $useOutdated < self::USE_EXPIRED && $optionsKey->expired( $article->getTouched() ) ) {
194  $this->incrementStats( $article, "miss.expired" );
195  $cacheTime = $optionsKey->getCacheTime();
196  wfDebugLog( "ParserCache",
197  "Parser options key expired, touched " . $article->getTouched()
198  . ", epoch {$this->cacheEpoch}, cached $cacheTime\n" );
199  return false;
200  } elseif ( $useOutdated < self::USE_OUTDATED &&
201  $optionsKey->isDifferentRevision( $article->getLatest() )
202  ) {
203  $this->incrementStats( $article, "miss.revid" );
204  $revId = $article->getLatest();
205  $cachedRevId = $optionsKey->getCacheRevisionId();
206  wfDebugLog( "ParserCache",
207  "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n"
208  );
209  return false;
210  }
211 
212  // $optionsKey->mUsedOptions is set by save() by calling ParserOutput::getUsedOptions()
213  $usedOptions = $optionsKey->mUsedOptions;
214  wfDebug( "Parser cache options found.\n" );
215  } else {
216  if ( $useOutdated < self::USE_ANYTHING ) {
217  return false;
218  }
219  $usedOptions = ParserOptions::allCacheVaryingOptions();
220  }
221 
222  return $this->getParserOutputKey(
223  $article,
224  $popts->optionsHash( $usedOptions, $article->getTitle() )
225  );
226  }
227 
238  public function get( $article, $popts, $useOutdated = false ) {
239  $canCache = $article->checkTouched();
240  if ( !$canCache ) {
241  // It's a redirect now
242  return false;
243  }
244 
245  $touched = $article->getTouched();
246 
247  $parserOutputKey = $this->getKey( $article, $popts,
248  $useOutdated ? self::USE_OUTDATED : self::USE_CURRENT_ONLY
249  );
250  if ( $parserOutputKey === false ) {
251  $this->incrementStats( $article, 'miss.absent' );
252  return false;
253  }
254 
255  $casToken = null;
257  $value = $this->cache->get( $parserOutputKey, BagOStuff::READ_VERIFIED );
258  if ( !$value ) {
259  wfDebug( "ParserOutput cache miss.\n" );
260  $this->incrementStats( $article, "miss.absent" );
261  return false;
262  }
263 
264  wfDebug( "ParserOutput cache found.\n" );
265 
266  $wikiPage = method_exists( $article, 'getPage' )
267  ? $article->getPage()
268  : $article;
269 
270  if ( !$useOutdated && $value->expired( $touched ) ) {
271  $this->incrementStats( $article, "miss.expired" );
272  $cacheTime = $value->getCacheTime();
273  wfDebugLog( "ParserCache",
274  "ParserOutput key expired, touched $touched, "
275  . "epoch {$this->cacheEpoch}, cached $cacheTime\n" );
276  $value = false;
277  } elseif ( !$useOutdated && $value->isDifferentRevision( $article->getLatest() ) ) {
278  $this->incrementStats( $article, "miss.revid" );
279  $revId = $article->getLatest();
280  $cachedRevId = $value->getCacheRevisionId();
281  wfDebugLog( "ParserCache",
282  "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n"
283  );
284  $value = false;
285  } elseif (
286  Hooks::run( 'RejectParserCacheValue', [ $value, $wikiPage, $popts ] ) === false
287  ) {
288  $this->incrementStats( $article, 'miss.rejected' );
289  wfDebugLog( "ParserCache",
290  "ParserOutput key valid, but rejected by RejectParserCacheValue hook handler.\n"
291  );
292  $value = false;
293  } else {
294  $this->incrementStats( $article, "hit" );
295  }
296 
297  return $value;
298  }
299 
307  public function save(
308  ParserOutput $parserOutput,
309  $page,
310  $popts,
311  $cacheTime = null,
312  $revId = null
313  ) {
314  if ( !$parserOutput->hasText() ) {
315  throw new InvalidArgumentException( 'Attempt to cache a ParserOutput with no text set!' );
316  }
317 
318  $expire = $parserOutput->getCacheExpiry();
319  if ( $expire > 0 && !$this->cache instanceof EmptyBagOStuff ) {
320  $cacheTime = $cacheTime ?: wfTimestampNow();
321  if ( !$revId ) {
322  $revision = $page->getRevision();
323  $revId = $revision ? $revision->getId() : null;
324  }
325 
326  $optionsKey = new CacheTime;
327  $optionsKey->mUsedOptions = $parserOutput->getUsedOptions();
328  $optionsKey->updateCacheExpiry( $expire );
329 
330  $optionsKey->setCacheTime( $cacheTime );
331  $parserOutput->setCacheTime( $cacheTime );
332  $optionsKey->setCacheRevisionId( $revId );
333  $parserOutput->setCacheRevisionId( $revId );
334 
335  $parserOutputKey = $this->getParserOutputKey( $page,
336  $popts->optionsHash( $optionsKey->mUsedOptions, $page->getTitle() ) );
337 
338  // Save the timestamp so that we don't have to load the revision row on view
339  $parserOutput->setTimestamp( $page->getTimestamp() );
340 
341  $msg = "Saved in parser cache with key $parserOutputKey" .
342  " and timestamp $cacheTime" .
343  " and revision id $revId" .
344  "\n";
345 
346  $parserOutput->mText .= "\n<!-- $msg -->\n";
347  wfDebug( $msg );
348 
349  // Save the parser output
350  $this->cache->set(
351  $parserOutputKey,
352  $parserOutput,
353  $expire,
355  );
356 
357  // ...and its pointer
358  $this->cache->set( $this->getOptionsKey( $page ), $optionsKey, $expire );
359 
360  Hooks::run(
361  'ParserCacheSaveComplete',
362  [ $this, $parserOutput, $page->getTitle(), $popts, $revId ]
363  );
364  } elseif ( $expire <= 0 ) {
365  wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
366  }
367  }
368 
376  public function getCacheStorage() {
377  return $this->cache;
378  }
379 }
ParserCache\USE_EXPIRED
const USE_EXPIRED
Use expired data if current data is unavailable.
Definition: ParserCache.php:40
CacheTime\getCacheExpiry
getCacheExpiry()
Returns the number of seconds after which this object should expire.
Definition: CacheTime.php:129
ParserCache\getOptionsKey
getOptionsKey( $page)
Definition: ParserCache.php:106
ParserOutput
Definition: ParserOutput.php:25
CacheTime
Parser cache specific expiry check.
Definition: CacheTime.php:29
EmptyBagOStuff
A BagOStuff object with no objects in it.
Definition: EmptyBagOStuff.php:29
ParserOutput\setTimestamp
setTimestamp( $timestamp)
Definition: ParserOutput.php:669
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:129
ParserOutput\getUsedOptions
getUsedOptions()
Returns the options from its ParserOptions which have been taken into account to produce this output.
Definition: ParserOutput.php:1096
true
return true
Definition: router.php:92
ParserCache\getETag
getETag( $article, $popts)
Provides an E-Tag suitable for the whole page.
Definition: ParserCache.php:132
BagOStuff\WRITE_ALLOW_SEGMENTS
const WRITE_ALLOW_SEGMENTS
Definition: BagOStuff.php:84
BagOStuff
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:63
ParserCache\$cache
BagOStuff $cache
Definition: ParserCache.php:52
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1007
CacheTime\setCacheTime
setCacheTime( $t)
setCacheTime() sets the timestamp expressing when the page has been rendered.
Definition: CacheTime.php:74
ParserCache\incrementStats
incrementStats( $article, $metricSuffix)
Definition: ParserCache.php:153
ParserCache\USE_ANYTHING
const USE_ANYTHING
Use expired data and data from different revisions, and if all else fails vary on all variable option...
Definition: ParserCache.php:49
wfIncrStats
wfIncrStats( $key, $count=1)
Increment a statistics counter.
Definition: GlobalFunctions.php:1160
ParserCache\getDirty
getDirty( $article, $popts)
Retrieve the ParserOutput from ParserCache, even if it's outdated.
Definition: ParserCache.php:144
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1900
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:913
ParserCache\getKey
getKey( $article, $popts, $useOutdated=self::USE_ANYTHING)
Generates a key for caching the given article considering the given parser options.
Definition: ParserCache.php:179
ParserCache\getCacheStorage
getCacheStorage()
Get the backend BagOStuff instance that powers the parser cache.
Definition: ParserCache.php:376
ParserCache\deleteOptionsKey
deleteOptionsKey( $page)
Definition: ParserCache.php:114
ParserCache\singleton
static singleton()
Get an instance of this object.
Definition: ParserCache.php:67
ParserCache\USE_CURRENT_ONLY
const USE_CURRENT_ONLY
Constants for self::getKey()
Definition: ParserCache.php:37
ParserCache\getParserOutputKey
getParserOutputKey( $article, $hash)
Definition: ParserCache.php:91
BagOStuff\READ_VERIFIED
const READ_VERIFIED
Definition: BagOStuff.php:80
ParserOptions\allCacheVaryingOptions
static allCacheVaryingOptions()
Return all option keys that vary the options hash.
Definition: ParserOptions.php:1295
ParserOutput\hasText
hasText()
Returns true if text was passed to the constructor, or set using setText().
Definition: ParserOutput.php:279
CacheTime\setCacheRevisionId
setCacheRevisionId( $id)
Definition: CacheTime.php:95
ParserCache\$cacheEpoch
string $cacheEpoch
Anything cached prior to this is invalidated.
Definition: ParserCache.php:59
ParserCache
Definition: ParserCache.php:30
ParserCache\save
save(ParserOutput $parserOutput, $page, $popts, $cacheTime=null, $revId=null)
Definition: ParserCache.php:307
wfWarn
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
Definition: GlobalFunctions.php:1065
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:729
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
ParserCache\USE_OUTDATED
const USE_OUTDATED
Use expired data or data from different revisions if current data is unavailable.
Definition: ParserCache.php:43
ParserCache\__construct
__construct(BagOStuff $cache, $cacheEpoch='20030516000000')
Setup a cache pathway with a given back-end storage mechanism.
Definition: ParserCache.php:81
ParserOptions\newFromUser
static newFromUser( $user)
Get a ParserOptions object from a given user.
Definition: ParserOptions.php:1027