MediaWiki REL1_28
ParserCache.php
Go to the documentation of this file.
1<?php
30 private $mMemc;
36 public static function singleton() {
37 static $instance;
38 if ( !isset( $instance ) ) {
40 $instance = new ParserCache( $parserMemc );
41 }
42 return $instance;
43 }
44
54 protected function __construct( BagOStuff $memCached ) {
55 $this->mMemc = $memCached;
56 }
57
63 protected function getParserOutputKey( $article, $hash ) {
65
66 // idhash seem to mean 'page id' + 'rendering hash' (r3710)
67 $pageid = $article->getId();
68 $renderkey = (int)( $wgRequest->getVal( 'action' ) == 'render' );
69
70 $key = wfMemcKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" );
71 return $key;
72 }
73
78 protected function getOptionsKey( $page ) {
79 return wfMemcKey( 'pcache', 'idoptions', $page->getId() );
80 }
81
86 public function deleteOptionsKey( $page ) {
87 $this->mMemc->delete( $this->getOptionsKey( $page ) );
88 }
89
104 public function getETag( $article, $popts ) {
105 return 'W/"' . $this->getParserOutputKey( $article,
106 $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) ) .
107 "--" . $article->getTouched() . '"';
108 }
109
116 public function getDirty( $article, $popts ) {
117 $value = $this->get( $article, $popts, true );
118 return is_object( $value ) ? $value : false;
119 }
120
140 public function getKey( $article, $popts, $useOutdated = true ) {
142
143 if ( $popts instanceof User ) {
144 wfWarn( "Use of outdated prototype ParserCache::getKey( &\$article, &\$user )\n" );
145 $popts = ParserOptions::newFromUser( $popts );
146 }
147
148 // Determine the options which affect this article
149 $casToken = null;
150 $optionsKey = $this->mMemc->get(
151 $this->getOptionsKey( $article ), $casToken, BagOStuff::READ_VERIFIED );
152 if ( $optionsKey instanceof CacheTime ) {
153 if ( !$useOutdated && $optionsKey->expired( $article->getTouched() ) ) {
154 wfIncrStats( "pcache.miss.expired" );
155 $cacheTime = $optionsKey->getCacheTime();
156 wfDebugLog( "ParserCache",
157 "Parser options key expired, touched " . $article->getTouched()
158 . ", epoch $wgCacheEpoch, cached $cacheTime\n" );
159 return false;
160 } elseif ( !$useOutdated && $optionsKey->isDifferentRevision( $article->getLatest() ) ) {
161 wfIncrStats( "pcache.miss.revid" );
162 $revId = $article->getLatest();
163 $cachedRevId = $optionsKey->getCacheRevisionId();
164 wfDebugLog( "ParserCache",
165 "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n"
166 );
167 return false;
168 }
169
170 // $optionsKey->mUsedOptions is set by save() by calling ParserOutput::getUsedOptions()
171 $usedOptions = $optionsKey->mUsedOptions;
172 wfDebug( "Parser cache options found.\n" );
173 } else {
174 if ( !$useOutdated ) {
175 return false;
176 }
177 $usedOptions = ParserOptions::legacyOptions();
178 }
179
180 return $this->getParserOutputKey(
181 $article,
182 $popts->optionsHash( $usedOptions, $article->getTitle() )
183 );
184 }
185
196 public function get( $article, $popts, $useOutdated = false ) {
198
199 $canCache = $article->checkTouched();
200 if ( !$canCache ) {
201 // It's a redirect now
202 return false;
203 }
204
205 $touched = $article->getTouched();
206
207 $parserOutputKey = $this->getKey( $article, $popts, $useOutdated );
208 if ( $parserOutputKey === false ) {
209 wfIncrStats( 'pcache.miss.absent' );
210 return false;
211 }
212
213 $casToken = null;
215 $value = $this->mMemc->get( $parserOutputKey, $casToken, BagOStuff::READ_VERIFIED );
216 if ( !$value ) {
217 wfDebug( "ParserOutput cache miss.\n" );
218 wfIncrStats( "pcache.miss.absent" );
219 return false;
220 }
221
222 wfDebug( "ParserOutput cache found.\n" );
223
224 // The edit section preference may not be the appropiate one in
225 // the ParserOutput, as we are not storing it in the parsercache
226 // key. Force it here. See bug 31445.
227 $value->setEditSectionTokens( $popts->getEditSection() );
228
229 $wikiPage = method_exists( $article, 'getPage' )
230 ? $article->getPage()
231 : $article;
232
233 if ( !$useOutdated && $value->expired( $touched ) ) {
234 wfIncrStats( "pcache.miss.expired" );
235 $cacheTime = $value->getCacheTime();
236 wfDebugLog( "ParserCache",
237 "ParserOutput key expired, touched $touched, "
238 . "epoch $wgCacheEpoch, cached $cacheTime\n" );
239 $value = false;
240 } elseif ( !$useOutdated && $value->isDifferentRevision( $article->getLatest() ) ) {
241 wfIncrStats( "pcache.miss.revid" );
242 $revId = $article->getLatest();
243 $cachedRevId = $value->getCacheRevisionId();
244 wfDebugLog( "ParserCache",
245 "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n"
246 );
247 $value = false;
248 } elseif (
249 Hooks::run( 'RejectParserCacheValue', [ $value, $wikiPage, $popts ] ) === false
250 ) {
251 wfIncrStats( 'pcache.miss.rejected' );
252 wfDebugLog( "ParserCache",
253 "ParserOutput key valid, but rejected by RejectParserCacheValue hook handler.\n"
254 );
255 $value = false;
256 } else {
257 wfIncrStats( "pcache.hit" );
258 }
259
260 return $value;
261 }
262
270 public function save( $parserOutput, $page, $popts, $cacheTime = null, $revId = null ) {
271 $expire = $parserOutput->getCacheExpiry();
272 if ( $expire > 0 && !$this->mMemc instanceof EmptyBagOStuff ) {
273 $cacheTime = $cacheTime ?: wfTimestampNow();
274 if ( !$revId ) {
275 $revision = $page->getRevision();
276 $revId = $revision ? $revision->getId() : null;
277 }
278
279 $optionsKey = new CacheTime;
280 $optionsKey->mUsedOptions = $parserOutput->getUsedOptions();
281 $optionsKey->updateCacheExpiry( $expire );
282
283 $optionsKey->setCacheTime( $cacheTime );
284 $parserOutput->setCacheTime( $cacheTime );
285 $optionsKey->setCacheRevisionId( $revId );
286 $parserOutput->setCacheRevisionId( $revId );
287
288 $parserOutputKey = $this->getParserOutputKey( $page,
289 $popts->optionsHash( $optionsKey->mUsedOptions, $page->getTitle() ) );
290
291 // Save the timestamp so that we don't have to load the revision row on view
292 $parserOutput->setTimestamp( $page->getTimestamp() );
293
294 $msg = "Saved in parser cache with key $parserOutputKey" .
295 " and timestamp $cacheTime" .
296 " and revision id $revId" .
297 "\n";
298
299 $parserOutput->mText .= "\n<!-- $msg -->\n";
300 wfDebug( $msg );
301
302 // Save the parser output
303 $this->mMemc->set( $parserOutputKey, $parserOutput, $expire );
304
305 // ...and its pointer
306 $this->mMemc->set( $this->getOptionsKey( $page ), $optionsKey, $expire );
307
308 Hooks::run(
309 'ParserCacheSaveComplete',
310 [ $this, $parserOutput, $page->getTitle(), $popts, $revId ]
311 );
312 } elseif ( $expire <= 0 ) {
313 wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
314 }
315 }
316}
$wgCacheEpoch
Set this to current time to invalidate all prior cached pages.
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.
wfIncrStats( $key, $count=1)
Increment a statistics counter.
wfMemcKey()
Make a cache key for the local wiki.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
if(! $wgDBerrorLogTZ) $wgRequest
Definition Setup.php:664
interface is intended to be more or less compatible with the PHP memcached client.
Definition BagOStuff.php:47
Parser cache specific expiry check.
Definition CacheTime.php:29
updateCacheExpiry( $seconds)
Sets the number of seconds after which this object should expire.
Definition CacheTime.php:93
A BagOStuff object with no objects in it.
getKey( $article, $popts, $useOutdated=true)
Generates a key for caching the given article considering the given parser options.
getDirty( $article, $popts)
Retrieve the ParserOutput from ParserCache, even if it's outdated.
getParserOutputKey( $article, $hash)
deleteOptionsKey( $page)
getOptionsKey( $page)
static singleton()
Get an instance of this object.
__construct(BagOStuff $memCached)
Setup a cache pathway with a given back-end storage mechanism.
BagOStuff $mMemc
save( $parserOutput, $page, $popts, $cacheTime=null, $revId=null)
getETag( $article, $popts)
Provides an E-Tag suitable for the whole page.
static legacyOptions()
Returns the full array of options that would have been used by in 1.16.
static newFromUser( $user)
Get a ParserOptions object from a given user.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:48
when a variable name is used in a it is silently declared as a new local masking the global
Definition design.txt:95
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context $revId
Definition hooks.txt:1095
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context $parserOutput
Definition hooks.txt:1090
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition hooks.txt:1950
Using a hook running we can avoid having all this option specific stuff in our mainline code Using the function array $article
Definition hooks.txt:78
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached $page
Definition hooks.txt:2534
processing should stop and the error should be shown to the user * false
Definition hooks.txt:189
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
controlled by $wgMainCacheType * $parserMemc
Definition memcached.txt:78