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 
67  private $stats;
68 
75  public static function singleton() {
76  wfDeprecated( __METHOD__, '1.30' );
77  return MediaWikiServices::getInstance()->getParserCache();
78  }
79 
92  public function __construct(
94  $cacheEpoch = '20030516000000',
95  HookContainer $hookContainer = null,
97  ) {
98  $this->cache = $cache;
99  $this->cacheEpoch = $cacheEpoch;
100  $this->hookRunner = new HookRunner(
101  $hookContainer ?: MediaWikiServices::getInstance()->getHookContainer()
102  );
103  $this->stats = $stats ?: MediaWikiServices::getInstance()->getStatsdDataFactory();
104  }
105 
111  protected function getParserOutputKey( WikiPage $wikiPage, $hash ) {
112  global $wgRequest;
113 
114  // idhash seem to mean 'page id' + 'rendering hash' (r3710)
115  $pageid = $wikiPage->getId();
116  $renderkey = (int)( $wgRequest->getVal( 'action' ) == 'render' );
117 
118  $key = $this->cache->makeKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" );
119  return $key;
120  }
121 
126  protected function getOptionsKey( WikiPage $wikiPage ) {
127  return $this->cache->makeKey( 'pcache', 'idoptions', $wikiPage->getId() );
128  }
129 
134  public function deleteOptionsKey( WikiPage $wikiPage ) {
135  $this->cache->delete( $this->getOptionsKey( $wikiPage ) );
136  }
137 
152  public function getETag( WikiPage $wikiPage, $popts ) {
153  return 'W/"'
154  . $this->getParserOutputKey(
155  $wikiPage,
156  $popts->optionsHash(
158  $wikiPage->getTitle()
159  )
160  )
161  . "--" . $wikiPage->getTouched() . '"';
162  }
163 
170  public function getDirty( WikiPage $wikiPage, $popts ) {
171  $value = $this->get( $wikiPage, $popts, true );
172  return is_object( $value ) ? $value : false;
173  }
174 
179  private function incrementStats( WikiPage $wikiPage, $metricSuffix ) {
180  $contentModel = str_replace( '.', '_', $wikiPage->getContentModel() );
181  $metricSuffix = str_replace( '.', '_', $metricSuffix );
182  $this->stats->increment( 'pcache.' . $contentModel . '.' . $metricSuffix );
183  }
184 
205  public function getKey( WikiPage $wikiPage, $popts, $useOutdated = self::USE_ANYTHING ) {
206  if ( is_bool( $useOutdated ) ) {
207  $useOutdated = $useOutdated ? self::USE_ANYTHING : self::USE_CURRENT_ONLY;
208  }
209 
210  if ( $popts instanceof User ) {
211  wfWarn( "Use of outdated prototype ParserCache::getKey( &\$wikiPage, &\$user )\n" );
212  $popts = ParserOptions::newFromUser( $popts );
213  }
214 
215  // Determine the options which affect this article
216  $optionsKey = $this->cache->get(
217  $this->getOptionsKey( $wikiPage ), BagOStuff::READ_VERIFIED );
218  if ( $optionsKey instanceof CacheTime ) {
219  if (
220  $useOutdated < self::USE_EXPIRED
221  && $optionsKey->expired( $wikiPage->getTouched() )
222  ) {
223  $this->incrementStats( $wikiPage, "miss.expired" );
224  $cacheTime = $optionsKey->getCacheTime();
225  wfDebugLog( "ParserCache",
226  "Parser options key expired, touched {$wikiPage->getTouched()}"
227  . ", epoch {$this->cacheEpoch}, cached $cacheTime" );
228  return false;
229  } elseif ( $useOutdated < self::USE_OUTDATED &&
230  $optionsKey->isDifferentRevision( $wikiPage->getLatest() )
231  ) {
232  $this->incrementStats( $wikiPage, "miss.revid" );
233  $revId = $wikiPage->getLatest();
234  $cachedRevId = $optionsKey->getCacheRevisionId();
235  wfDebugLog( "ParserCache",
236  "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId"
237  );
238  return false;
239  }
240 
241  // $optionsKey->mUsedOptions is set by save() by calling ParserOutput::getUsedOptions()
242  $usedOptions = $optionsKey->mUsedOptions;
243  wfDebug( "Parser cache options found." );
244  } else {
245  if ( $useOutdated < self::USE_ANYTHING ) {
246  return false;
247  }
248  $usedOptions = ParserOptions::allCacheVaryingOptions();
249  }
250 
251  return $this->getParserOutputKey(
252  $wikiPage,
253  $popts->optionsHash( $usedOptions, $wikiPage->getTitle() )
254  );
255  }
256 
267  public function get( Page $wikiPage, $popts, $useOutdated = false ) {
268  if ( $wikiPage instanceof Article ) {
269  wfDeprecated(
270  __METHOD__ . ' with Article parameter',
271  1.35
272  );
273  $wikiPage = $wikiPage->getPage();
274  }
275 
276  $canCache = $wikiPage->checkTouched();
277  if ( !$canCache ) {
278  // It's a redirect now
279  return false;
280  }
281 
282  $touched = $wikiPage->getTouched();
283 
284  $parserOutputKey = $this->getKey( $wikiPage, $popts,
285  $useOutdated ? self::USE_OUTDATED : self::USE_CURRENT_ONLY
286  );
287  if ( $parserOutputKey === false ) {
288  $this->incrementStats( $wikiPage, 'miss.absent' );
289  return false;
290  }
291 
292  $casToken = null;
294  $value = $this->cache->get( $parserOutputKey, BagOStuff::READ_VERIFIED );
295  if ( !$value ) {
296  wfDebug( "ParserOutput cache miss." );
297  $this->incrementStats( $wikiPage, "miss.absent" );
298  return false;
299  }
300 
301  wfDebug( "ParserOutput cache found." );
302 
303  if ( !$useOutdated && $value->expired( $touched ) ) {
304  $this->incrementStats( $wikiPage, "miss.expired" );
305  $cacheTime = $value->getCacheTime();
306  wfDebugLog( "ParserCache",
307  "ParserOutput key expired, touched $touched, "
308  . "epoch {$this->cacheEpoch}, cached $cacheTime" );
309  $value = false;
310  } elseif (
311  !$useOutdated
312  && $value->isDifferentRevision( $wikiPage->getLatest() )
313  ) {
314  $this->incrementStats( $wikiPage, "miss.revid" );
315  $revId = $wikiPage->getLatest();
316  $cachedRevId = $value->getCacheRevisionId();
317  wfDebugLog( "ParserCache",
318  "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId"
319  );
320  $value = false;
321  } elseif (
322  $this->hookRunner->onRejectParserCacheValue( $value, $wikiPage, $popts ) === false
323  ) {
324  $this->incrementStats( $wikiPage, 'miss.rejected' );
325  wfDebugLog( "ParserCache",
326  "ParserOutput key valid, but rejected by RejectParserCacheValue hook handler."
327  );
328  $value = false;
329  } else {
330  $this->incrementStats( $wikiPage, "hit" );
331  }
332 
333  return $value;
334  }
335 
343  public function save(
344  ParserOutput $parserOutput,
345  WikiPage $wikiPage,
346  $popts,
347  $cacheTime = null,
348  $revId = null
349  ) {
350  if ( !$parserOutput->hasText() ) {
351  throw new InvalidArgumentException( 'Attempt to cache a ParserOutput with no text set!' );
352  }
353 
354  $expire = $parserOutput->getCacheExpiry();
355  if ( $expire > 0 && !$this->cache instanceof EmptyBagOStuff ) {
356  $cacheTime = $cacheTime ?: wfTimestampNow();
357  if ( !$revId ) {
358  $revision = $wikiPage->getRevisionRecord();
359  $revId = $revision ? $revision->getId() : null;
360  }
361 
362  $optionsKey = new CacheTime;
363  $optionsKey->mUsedOptions = $parserOutput->getUsedOptions();
364  $optionsKey->updateCacheExpiry( $expire );
365 
366  $optionsKey->setCacheTime( $cacheTime );
367  $parserOutput->setCacheTime( $cacheTime );
368  $optionsKey->setCacheRevisionId( $revId );
369  $parserOutput->setCacheRevisionId( $revId );
370 
371  $parserOutputKey = $this->getParserOutputKey( $wikiPage,
372  $popts->optionsHash( $optionsKey->mUsedOptions, $wikiPage->getTitle() ) );
373 
374  // Save the timestamp so that we don't have to load the revision row on view
375  $parserOutput->setTimestamp( $wikiPage->getTimestamp() );
376 
377  $msg = "Saved in parser cache with key $parserOutputKey" .
378  " and timestamp $cacheTime" .
379  " and revision id $revId";
380 
381  $parserOutput->mText .= "\n<!-- $msg\n -->\n";
382  wfDebug( $msg );
383 
384  // Save the parser output
385  $this->cache->set(
386  $parserOutputKey,
387  $parserOutput,
388  $expire,
390  );
391 
392  // ...and its pointer
393  $this->cache->set( $this->getOptionsKey( $wikiPage ), $optionsKey, $expire );
394 
395  $this->hookRunner->onParserCacheSaveComplete(
396  $this, $parserOutput, $wikiPage->getTitle(), $popts, $revId );
397  } elseif ( $expire <= 0 ) {
398  wfDebug( "Parser output was marked as uncacheable and has not been saved." );
399  }
400  }
401 
409  public function getCacheStorage() {
410  return $this->cache;
411  }
412 }
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:744
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:729
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:155
ParserOutput\getUsedOptions
getUsedOptions()
Returns the options from its ParserOptions which have been taken into account to produce this output.
Definition: ParserOutput.php:1157
ParserCache\deleteOptionsKey
deleteOptionsKey(WikiPage $wikiPage)
Definition: ParserCache.php:134
ParserCache\getParserOutputKey
getParserOutputKey(WikiPage $wikiPage, $hash)
Definition: ParserCache.php:111
true
return true
Definition: router.php:90
WikiPage\getTouched
getTouched()
Get the page_touched field.
Definition: WikiPage.php:637
BagOStuff\WRITE_ALLOW_SEGMENTS
const WRITE_ALLOW_SEGMENTS
Definition: BagOStuff.php:92
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:205
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:52
BagOStuff
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:71
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:152
ParserCache\incrementStats
incrementStats(WikiPage $wikiPage, $metricSuffix)
Definition: ParserCache.php:179
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:988
ParserCache\$stats
IBufferingStatsdDataFactory $stats
Definition: ParserCache.php:67
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:1026
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
ParserCache\getOptionsKey
getOptionsKey(WikiPage $wikiPage)
Definition: ParserCache.php:126
WikiPage\getId
getId()
Definition: WikiPage.php:541
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:281
ParserCache\$hookRunner
HookRunner $hookRunner
Definition: ParserCache.php:64
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1837
WikiPage\getLatest
getLatest()
Get the page_latest field.
Definition: WikiPage.php:659
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:909
WikiPage\getContentModel
getContentModel()
Returns the page's content model id (see the CONTENT_MODEL_XXX constants).
Definition: WikiPage.php:593
ParserCache\getCacheStorage
getCacheStorage()
Get the backend BagOStuff instance that powers the parser cache.
Definition: ParserCache.php:409
ParserCache\singleton
static singleton()
Get an instance of this object.
Definition: ParserCache.php:75
ParserCache\USE_CURRENT_ONLY
const USE_CURRENT_ONLY
Constants for self::getKey()
Definition: ParserCache.php:39
IBufferingStatsdDataFactory
MediaWiki adaptation of StatsdDataFactory that provides buffering functionality.
Definition: IBufferingStatsdDataFactory.php:13
ParserCache\save
save(ParserOutput $parserOutput, WikiPage $wikiPage, $popts, $cacheTime=null, $revId=null)
Definition: ParserCache.php:343
BagOStuff\READ_VERIFIED
const READ_VERIFIED
Definition: BagOStuff.php:88
ParserOptions\allCacheVaryingOptions
static allCacheVaryingOptions()
Return all option keys that vary the options hash.
Definition: ParserOptions.php:1349
ParserOutput\hasText
hasText()
Returns true if text was passed to the constructor, or set using setText().
Definition: ParserOutput.php:300
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:170
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:1073
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:569
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:46
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:644
WikiPage\getTimestamp
getTimestamp()
Definition: WikiPage.php:776
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
ParserCache\__construct
__construct(BagOStuff $cache, $cacheEpoch='20030516000000', HookContainer $hookContainer=null, IBufferingStatsdDataFactory $stats=null)
Setup a cache pathway with a given back-end storage mechanism.
Definition: ParserCache.php:92
ParserOptions\newFromUser
static newFromUser( $user)
Get a ParserOptions object from a given user.
Definition: ParserOptions.php:1077