MediaWiki  master
ParserCache.php
Go to the documentation of this file.
1 <?php
27 
32 class ParserCache {
39  private const USE_CURRENT_ONLY = 0;
40 
42  private const USE_EXPIRED = 1;
43 
45  private const USE_OUTDATED = 2;
46 
51  private const USE_ANYTHING = 3;
52 
54  private $cache;
55 
61  private $cacheEpoch;
62 
64  private $hookRunner;
65 
72  public static function singleton() {
73  wfDeprecated( __METHOD__, '1.30' );
74  return MediaWikiServices::getInstance()->getParserCache();
75  }
76 
88  public function __construct( BagOStuff $cache, $cacheEpoch = '20030516000000',
89  HookContainer $hookContainer = null
90  ) {
91  $this->cache = $cache;
92  $this->cacheEpoch = $cacheEpoch;
93  $this->hookRunner = new HookRunner(
94  $hookContainer ?: MediaWikiServices::getInstance()->getHookContainer()
95  );
96  }
97 
103  protected function getParserOutputKey( WikiPage $wikiPage, $hash ) {
104  global $wgRequest;
105 
106  // idhash seem to mean 'page id' + 'rendering hash' (r3710)
107  $pageid = $wikiPage->getId();
108  $renderkey = (int)( $wgRequest->getVal( 'action' ) == 'render' );
109 
110  $key = $this->cache->makeKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" );
111  return $key;
112  }
113 
118  protected function getOptionsKey( WikiPage $wikiPage ) {
119  return $this->cache->makeKey( 'pcache', 'idoptions', $wikiPage->getId() );
120  }
121 
126  public function deleteOptionsKey( WikiPage $wikiPage ) {
127  $this->cache->delete( $this->getOptionsKey( $wikiPage ) );
128  }
129 
144  public function getETag( WikiPage $wikiPage, $popts ) {
145  return 'W/"'
146  . $this->getParserOutputKey(
147  $wikiPage,
148  $popts->optionsHash(
150  $wikiPage->getTitle()
151  )
152  )
153  . "--" . $wikiPage->getTouched() . '"';
154  }
155 
162  public function getDirty( WikiPage $wikiPage, $popts ) {
163  $value = $this->get( $wikiPage, $popts, true );
164  return is_object( $value ) ? $value : false;
165  }
166 
171  private function incrementStats( WikiPage $wikiPage, $metricSuffix ) {
172  $contentModel = str_replace( '.', '_', $wikiPage->getContentModel() );
173  $metricSuffix = str_replace( '.', '_', $metricSuffix );
174  wfIncrStats( 'pcache.' . $contentModel . '.' . $metricSuffix );
175  }
176 
197  public function getKey( WikiPage $wikiPage, $popts, $useOutdated = self::USE_ANYTHING ) {
198  if ( is_bool( $useOutdated ) ) {
199  $useOutdated = $useOutdated ? self::USE_ANYTHING : self::USE_CURRENT_ONLY;
200  }
201 
202  if ( $popts instanceof User ) {
203  wfWarn( "Use of outdated prototype ParserCache::getKey( &\$wikiPage, &\$user )\n" );
204  $popts = ParserOptions::newFromUser( $popts );
205  }
206 
207  // Determine the options which affect this article
208  $optionsKey = $this->cache->get(
209  $this->getOptionsKey( $wikiPage ), BagOStuff::READ_VERIFIED );
210  if ( $optionsKey instanceof CacheTime ) {
211  if (
212  $useOutdated < self::USE_EXPIRED
213  && $optionsKey->expired( $wikiPage->getTouched() )
214  ) {
215  $this->incrementStats( $wikiPage, "miss.expired" );
216  $cacheTime = $optionsKey->getCacheTime();
217  wfDebugLog( "ParserCache",
218  "Parser options key expired, touched {$wikiPage->getTouched()}"
219  . ", epoch {$this->cacheEpoch}, cached $cacheTime" );
220  return false;
221  } elseif ( $useOutdated < self::USE_OUTDATED &&
222  $optionsKey->isDifferentRevision( $wikiPage->getLatest() )
223  ) {
224  $this->incrementStats( $wikiPage, "miss.revid" );
225  $revId = $wikiPage->getLatest();
226  $cachedRevId = $optionsKey->getCacheRevisionId();
227  wfDebugLog( "ParserCache",
228  "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId"
229  );
230  return false;
231  }
232 
233  // $optionsKey->mUsedOptions is set by save() by calling ParserOutput::getUsedOptions()
234  $usedOptions = $optionsKey->mUsedOptions;
235  wfDebug( "Parser cache options found." );
236  } else {
237  if ( $useOutdated < self::USE_ANYTHING ) {
238  return false;
239  }
240  $usedOptions = ParserOptions::allCacheVaryingOptions();
241  }
242 
243  return $this->getParserOutputKey(
244  $wikiPage,
245  $popts->optionsHash( $usedOptions, $wikiPage->getTitle() )
246  );
247  }
248 
259  public function get( Page $wikiPage, $popts, $useOutdated = false ) {
260  if ( $wikiPage instanceof Article ) {
261  wfDeprecated(
262  __METHOD__ . ' with Article parameter',
263  1.35
264  );
265  $wikiPage = $wikiPage->getPage();
266  }
267 
268  $canCache = $wikiPage->checkTouched();
269  if ( !$canCache ) {
270  // It's a redirect now
271  return false;
272  }
273 
274  $touched = $wikiPage->getTouched();
275 
276  $parserOutputKey = $this->getKey( $wikiPage, $popts,
277  $useOutdated ? self::USE_OUTDATED : self::USE_CURRENT_ONLY
278  );
279  if ( $parserOutputKey === false ) {
280  $this->incrementStats( $wikiPage, 'miss.absent' );
281  return false;
282  }
283 
284  $casToken = null;
286  $value = $this->cache->get( $parserOutputKey, BagOStuff::READ_VERIFIED );
287  if ( !$value ) {
288  wfDebug( "ParserOutput cache miss." );
289  $this->incrementStats( $wikiPage, "miss.absent" );
290  return false;
291  }
292 
293  wfDebug( "ParserOutput cache found." );
294 
295  if ( !$useOutdated && $value->expired( $touched ) ) {
296  $this->incrementStats( $wikiPage, "miss.expired" );
297  $cacheTime = $value->getCacheTime();
298  wfDebugLog( "ParserCache",
299  "ParserOutput key expired, touched $touched, "
300  . "epoch {$this->cacheEpoch}, cached $cacheTime" );
301  $value = false;
302  } elseif (
303  !$useOutdated
304  && $value->isDifferentRevision( $wikiPage->getLatest() )
305  ) {
306  $this->incrementStats( $wikiPage, "miss.revid" );
307  $revId = $wikiPage->getLatest();
308  $cachedRevId = $value->getCacheRevisionId();
309  wfDebugLog( "ParserCache",
310  "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId"
311  );
312  $value = false;
313  } elseif (
314  $this->hookRunner->onRejectParserCacheValue( $value, $wikiPage, $popts ) === false
315  ) {
316  $this->incrementStats( $wikiPage, 'miss.rejected' );
317  wfDebugLog( "ParserCache",
318  "ParserOutput key valid, but rejected by RejectParserCacheValue hook handler."
319  );
320  $value = false;
321  } else {
322  $this->incrementStats( $wikiPage, "hit" );
323  }
324 
325  return $value;
326  }
327 
335  public function save(
336  ParserOutput $parserOutput,
337  WikiPage $wikiPage,
338  $popts,
339  $cacheTime = null,
340  $revId = null
341  ) {
342  if ( !$parserOutput->hasText() ) {
343  throw new InvalidArgumentException( 'Attempt to cache a ParserOutput with no text set!' );
344  }
345 
346  $expire = $parserOutput->getCacheExpiry();
347  if ( $expire > 0 && !$this->cache instanceof EmptyBagOStuff ) {
348  $cacheTime = $cacheTime ?: wfTimestampNow();
349  if ( !$revId ) {
350  $revision = $wikiPage->getRevisionRecord();
351  $revId = $revision ? $revision->getId() : null;
352  }
353 
354  $optionsKey = new CacheTime;
355  $optionsKey->mUsedOptions = $parserOutput->getUsedOptions();
356  $optionsKey->updateCacheExpiry( $expire );
357 
358  $optionsKey->setCacheTime( $cacheTime );
359  $parserOutput->setCacheTime( $cacheTime );
360  $optionsKey->setCacheRevisionId( $revId );
361  $parserOutput->setCacheRevisionId( $revId );
362 
363  $parserOutputKey = $this->getParserOutputKey( $wikiPage,
364  $popts->optionsHash( $optionsKey->mUsedOptions, $wikiPage->getTitle() ) );
365 
366  // Save the timestamp so that we don't have to load the revision row on view
367  $parserOutput->setTimestamp( $wikiPage->getTimestamp() );
368 
369  $msg = "Saved in parser cache with key $parserOutputKey" .
370  " and timestamp $cacheTime" .
371  " and revision id $revId";
372 
373  $parserOutput->mText .= "\n<!-- $msg\n -->\n";
374  wfDebug( $msg );
375 
376  // Save the parser output
377  $this->cache->set(
378  $parserOutputKey,
379  $parserOutput,
380  $expire,
382  );
383 
384  // ...and its pointer
385  $this->cache->set( $this->getOptionsKey( $wikiPage ), $optionsKey, $expire );
386 
387  $this->hookRunner->onParserCacheSaveComplete(
388  $this, $parserOutput, $wikiPage->getTitle(), $popts, $revId );
389  } elseif ( $expire <= 0 ) {
390  wfDebug( "Parser output was marked as uncacheable and has not been saved." );
391  }
392  }
393 
401  public function getCacheStorage() {
402  return $this->cache;
403  }
404 }
ParserCache\USE_EXPIRED
const USE_EXPIRED
Use expired data if current data is unavailable.
Definition: ParserCache.php:42
Page
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: Page.php:30
CacheTime\getCacheExpiry
getCacheExpiry()
Returns the number of seconds after which this object should expire.
Definition: CacheTime.php:129
WikiPage\getRevisionRecord
getRevisionRecord()
Get the latest revision.
Definition: WikiPage.php:773
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:715
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:149
ParserOutput\getUsedOptions
getUsedOptions()
Returns the options from its ParserOptions which have been taken into account to produce this output.
Definition: ParserOutput.php:1142
ParserCache\deleteOptionsKey
deleteOptionsKey(WikiPage $wikiPage)
Definition: ParserCache.php:126
ParserCache\getParserOutputKey
getParserOutputKey(WikiPage $wikiPage, $hash)
Definition: ParserCache.php:103
true
return true
Definition: router.php:90
WikiPage\getTouched
getTouched()
Get the page_touched field.
Definition: WikiPage.php:666
BagOStuff\WRITE_ALLOW_SEGMENTS
const WRITE_ALLOW_SEGMENTS
Definition: BagOStuff.php:91
ParserCache\getKey
getKey(WikiPage $wikiPage, $popts, $useOutdated=self::USE_ANYTHING)
Generates a key for caching the given page considering the given parser options.
Definition: ParserCache.php:197
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:49
BagOStuff
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:70
ParserCache\$cache
BagOStuff $cache
Definition: ParserCache.php:54
ParserCache\getETag
getETag(WikiPage $wikiPage, $popts)
Provides an E-Tag suitable for the whole page.
Definition: ParserCache.php:144
ParserCache\incrementStats
incrementStats(WikiPage $wikiPage, $metricSuffix)
Definition: ParserCache.php:171
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:992
CacheTime\setCacheTime
setCacheTime( $t)
setCacheTime() sets the timestamp expressing when the page has been rendered.
Definition: CacheTime.php:74
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1030
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:51
wfIncrStats
wfIncrStats( $key, $count=1)
Increment a statistics counter.
Definition: GlobalFunctions.php:1094
ParserCache\getOptionsKey
getOptionsKey(WikiPage $wikiPage)
Definition: ParserCache.php:118
WikiPage\getId
getId()
Definition: WikiPage.php:570
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:310
ParserCache\$hookRunner
HookRunner $hookRunner
Definition: ParserCache.php:64
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1835
WikiPage\getLatest
getLatest()
Get the page_latest field.
Definition: WikiPage.php:688
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
WikiPage\getContentModel
getContentModel()
Returns the page's content model id (see the CONTENT_MODEL_XXX constants).
Definition: WikiPage.php:622
ParserCache\getCacheStorage
getCacheStorage()
Get the backend BagOStuff instance that powers the parser cache.
Definition: ParserCache.php:401
ParserCache\singleton
static singleton()
Get an instance of this object.
Definition: ParserCache.php:72
ParserCache\__construct
__construct(BagOStuff $cache, $cacheEpoch='20030516000000', HookContainer $hookContainer=null)
Setup a cache pathway with a given back-end storage mechanism.
Definition: ParserCache.php:88
ParserCache\USE_CURRENT_ONLY
const USE_CURRENT_ONLY
Constants for self::getKey()
Definition: ParserCache.php:39
ParserCache\save
save(ParserOutput $parserOutput, WikiPage $wikiPage, $popts, $cacheTime=null, $revId=null)
Definition: ParserCache.php:335
BagOStuff\READ_VERIFIED
const READ_VERIFIED
Definition: BagOStuff.php:87
ParserOptions\allCacheVaryingOptions
static allCacheVaryingOptions()
Return all option keys that vary the options hash.
Definition: ParserOptions.php:1347
ParserOutput\hasText
hasText()
Returns true if text was passed to the constructor, or set using setText().
Definition: ParserOutput.php:294
CacheTime\setCacheRevisionId
setCacheRevisionId( $id)
Definition: CacheTime.php:95
ParserCache\$cacheEpoch
string $cacheEpoch
Anything cached prior to this is invalidated.
Definition: ParserCache.php:61
ParserCache
Definition: ParserCache.php:32
ParserCache\getDirty
getDirty(WikiPage $wikiPage, $popts)
Retrieve the ParserOutput from ParserCache, even if it's outdated.
Definition: ParserCache.php:162
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:44
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:1051
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:563
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:46
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:643
WikiPage\getTimestamp
getTimestamp()
Definition: WikiPage.php:805
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:55
ParserCache\USE_OUTDATED
const USE_OUTDATED
Use expired data or data from different revisions if current data is unavailable.
Definition: ParserCache.php:45
ParserOptions\newFromUser
static newFromUser( $user)
Get a ParserOptions object from a given user.
Definition: ParserOptions.php:1079