MediaWiki REL1_35
ParserCache.php
Go to the documentation of this file.
1<?php
27
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,
96 IBufferingStatsdDataFactory $stats = 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(
157 ParserOptions::allCacheVaryingOptions(),
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 ) {
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,
389 BagOStuff::WRITE_ALLOW_SEGMENTS
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}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
if(! $wgDBerrorLogTZ) $wgRequest
Definition Setup.php:643
Class for viewing MediaWiki article and history.
Definition Article.php:46
Class representing a cache/ephemeral data store.
Definition BagOStuff.php:71
Parser cache specific expiry check.
Definition CacheTime.php:29
setCacheRevisionId( $id)
Definition CacheTime.php:95
setCacheTime( $t)
setCacheTime() sets the timestamp expressing when the page has been rendered.
Definition CacheTime.php:74
getCacheExpiry()
Returns the number of seconds after which this object should expire.
A BagOStuff object with no objects in it.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
MediaWikiServices is the service locator for the application scope of MediaWiki.
const USE_EXPIRED
Use expired data if current data is unavailable.
getETag(WikiPage $wikiPage, $popts)
Provides an E-Tag suitable for the whole page.
static singleton()
Get an instance of this object.
getCacheStorage()
Get the backend BagOStuff instance that powers the parser cache.
getOptionsKey(WikiPage $wikiPage)
const USE_ANYTHING
Use expired data and data from different revisions, and if all else fails vary on all variable option...
save(ParserOutput $parserOutput, WikiPage $wikiPage, $popts, $cacheTime=null, $revId=null)
getParserOutputKey(WikiPage $wikiPage, $hash)
string $cacheEpoch
Anything cached prior to this is invalidated.
const USE_CURRENT_ONLY
Constants for self::getKey()
incrementStats(WikiPage $wikiPage, $metricSuffix)
IBufferingStatsdDataFactory $stats
HookRunner $hookRunner
const USE_OUTDATED
Use expired data or data from different revisions if current data is unavailable.
getKey(WikiPage $wikiPage, $popts, $useOutdated=self::USE_ANYTHING)
Generates a key for caching the given page considering the given parser options.
getDirty(WikiPage $wikiPage, $popts)
Retrieve the ParserOutput from ParserCache, even if it's outdated.
__construct(BagOStuff $cache, $cacheEpoch='20030516000000', HookContainer $hookContainer=null, IBufferingStatsdDataFactory $stats=null)
Setup a cache pathway with a given back-end storage mechanism.
BagOStuff $cache
deleteOptionsKey(WikiPage $wikiPage)
hasText()
Returns true if text was passed to the constructor, or set using setText().
getUsedOptions()
Returns the options from its ParserOptions which have been taken into account to produce this output.
setTimestamp( $timestamp)
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:60
Class representing a MediaWiki article and history.
Definition WikiPage.php:51
getTimestamp()
Definition WikiPage.php:813
getLatest()
Get the page_latest field.
Definition WikiPage.php:696
getContentModel()
Returns the page's content model id (see the CONTENT_MODEL_XXX constants).
Definition WikiPage.php:630
getTitle()
Get the title object of the article.
Definition WikiPage.php:318
getRevisionRecord()
Get the latest revision.
Definition WikiPage.php:781
getTouched()
Get the page_touched field.
Definition WikiPage.php:674
MediaWiki adaptation of StatsdDataFactory that provides buffering functionality.
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition Page.php:30
$cache
Definition mcc.php:33
return true
Definition router.php:92