23use InvalidArgumentException;
38use Wikimedia\Assert\Assert;
55 public const OPT_NO_CHECK_CACHE = 1;
58 public const OPT_FORCE_PARSE = self::OPT_NO_CHECK_CACHE;
63 public const OPT_NO_UPDATE_CACHE = 2;
70 public const OPT_NO_AUDIENCE_CHECK = 4;
76 public const OPT_NO_CACHE = self::OPT_NO_UPDATE_CACHE | self::OPT_NO_CHECK_CACHE;
82 private const CACHE_PRIMARY =
'primary';
85 private const CACHE_SECONDARY =
'secondary';
88 private $primaryCache;
93 private $secondaryCache;
100 private $localCache = [];
103 private $revisionLookup;
106 private $revisionRenderer;
109 private $statsDataFactory;
118 private $wikiPageFactory;
121 private $titleFormatter;
141 LoggerSpi $loggerSpi,
145 $this->primaryCache = $primaryCache;
146 $this->secondaryCache = $secondaryCache;
147 $this->revisionLookup = $revisionLookup;
148 $this->revisionRenderer = $revisionRenderer;
149 $this->statsDataFactory = $statsDataFactory;
150 $this->lbFactory = $lbFactory;
151 $this->loggerSpi = $loggerSpi;
152 $this->wikiPageFactory = $wikiPageFactory;
153 $this->titleFormatter = $titleFormatter;
164 private function shouldUseCache(
168 if ( $rev && !$rev->
getId() ) {
177 $wikiPage = $this->wikiPageFactory->newFromTitle( $page );
178 if ( !$page->
exists() || !$wikiPage->getContentHandler()->isParserCacheSupported() ) {
184 return self::CACHE_PRIMARY;
187 if ( !$rev->
audienceCan( RevisionRecord::DELETED_TEXT, RevisionRecord::FOR_PUBLIC ) ) {
192 return self::CACHE_SECONDARY;
211 $isOld = $revision && $revision->getId() !== $page->getLatest();
212 $useCache = $this->shouldUseCache( $page, $revision );
213 $classCacheKey = $this->primaryCache->makeParserOutputKey( $page, $parserOptions );
215 if ( $useCache === self::CACHE_PRIMARY ) {
216 if ( isset( $this->localCache[$classCacheKey] ) && !$isOld ) {
217 return $this->localCache[$classCacheKey];
219 $output = $this->primaryCache->get( $page, $parserOptions );
220 } elseif ( $useCache === self::CACHE_SECONDARY && $revision ) {
221 $output = $this->secondaryCache->get( $revision, $parserOptions );
226 if ( $output && !$isOld ) {
227 $this->localCache[$classCacheKey] = $output;
231 $this->statsDataFactory->increment(
"ParserOutputAccess.Cache.$useCache.hit" );
233 $this->statsDataFactory->increment(
"ParserOutputAccess.Cache.$useCache.miss" );
236 return $output ?:
null;
267 $error = $this->checkPreconditions( $page, $revision, $options );
269 $this->statsDataFactory->increment(
"ParserOutputAccess.Case.error" );
273 $isOld = $revision && $revision->getId() !== $page->
getLatest();
275 $this->statsDataFactory->increment(
'ParserOutputAccess.Case.old' );
277 $this->statsDataFactory->increment(
'ParserOutputAccess.Case.current' );
280 if ( !( $options & self::OPT_NO_CHECK_CACHE ) ) {
281 $output = $this->getCachedParserOutput( $page, $parserOptions, $revision );
283 return Status::newGood( $output );
289 $revision = $revId ? $this->revisionLookup->getRevisionById( $revId ) :
null;
292 $this->statsDataFactory->increment(
"ParserOutputAccess.Status.norev" );
297 $work = $this->newPoolWorkArticleView( $page, $parserOptions, $revision, $options );
299 $status = $work->execute();
300 $output = $status->getValue();
301 Assert::postcondition( $output || !$status->isOK(),
'Worker returned invalid status' );
303 if ( $output && !$isOld ) {
304 $classCacheKey = $this->primaryCache->makeParserOutputKey( $page, $parserOptions );
305 $this->localCache[$classCacheKey] = $output;
308 if ( $status->isGood() ) {
309 $this->statsDataFactory->increment(
'ParserOutputAccess.Status.good' );
310 } elseif ( $status->isOK() ) {
311 $this->statsDataFactory->increment(
'ParserOutputAccess.Status.ok' );
313 $this->statsDataFactory->increment(
'ParserOutputAccess.Status.error' );
326 private function checkPreconditions(
328 ?RevisionRecord $revision =
null,
331 if ( !$page->exists() ) {
335 if ( !( $options & self::OPT_NO_UPDATE_CACHE ) && $revision && !$revision->getId() ) {
336 throw new InvalidArgumentException(
337 'The revision does not have a known ID. Use NO_CACHE.'
341 if ( $revision && $revision->getPageId() !== $page->
getId() ) {
342 throw new InvalidArgumentException(
343 'The revision does not belong to the given page.'
347 if ( $revision && !( $options & self::OPT_NO_AUDIENCE_CHECK ) ) {
350 if ( !$revision->audienceCan( RevisionRecord::DELETED_TEXT, RevisionRecord::FOR_PUBLIC ) ) {
352 'missing-revision-permission',
354 $revision->getTimestamp(),
355 $this->titleFormatter->getPrefixedDBkey( $page )
371 private function newPoolWorkArticleView(
374 RevisionRecord $revision,
377 $useCache = $this->shouldUseCache( $page, $revision );
379 switch ( $useCache ) {
380 case self::CACHE_PRIMARY:
381 $this->statsDataFactory->increment(
'ParserOutputAccess.PoolWork.Current' );
382 $parserCacheMetadata = $this->primaryCache->getMetadata( $page );
383 $cacheKey = $this->primaryCache->makeParserOutputKey( $page, $parserOptions,
384 $parserCacheMetadata ? $parserCacheMetadata->getUsedOptions() : null
387 $workKey = $cacheKey .
':revid:' . $revision->getId();
394 $this->revisionRenderer,
398 $this->wikiPageFactory,
399 !( $options & self::OPT_NO_UPDATE_CACHE )
402 case self::CACHE_SECONDARY:
403 $this->statsDataFactory->increment(
'ParserOutputAccess.PoolWork.Old' );
404 $workKey = $this->secondaryCache->makeParserOutputKey( $revision, $parserOptions );
407 $this->secondaryCache,
410 $this->revisionRenderer,
415 $this->statsDataFactory->increment(
'ParserOutputAccess.PoolWork.Uncached' );
416 $workKey = $this->secondaryCache->makeParserOutputKeyOptionalRevId( $revision, $parserOptions );
421 $this->revisionRenderer,
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Service for getting rendered output of a given page.
__construct(ParserCache $primaryCache, RevisionOutputCache $secondaryCache, RevisionLookup $revisionLookup, RevisionRenderer $revisionRenderer, IBufferingStatsdDataFactory $statsDataFactory, ILBFactory $lbFactory, LoggerSpi $loggerSpi, WikiPageFactory $wikiPageFactory, TitleFormatter $titleFormatter)
getCachedParserOutput(PageRecord $page, ParserOptions $parserOptions, ?RevisionRecord $revision=null, int $options=0)
Returns the rendered output for the given page if it is present in the cache.
getParserOutput(PageRecord $page, ParserOptions $parserOptions, ?RevisionRecord $revision=null, int $options=0)
Returns the rendered output for the given page.
Service for creating WikiPage objects.
Cache for ParserOutput objects corresponding to the latest page revisions.
Set options of the Parser.
Class for dealing with PoolCounters using class members.
PoolWorkArticleView for the current revision of a page, using ParserCache.
PoolWorkArticleView for an old revision of a page, using a simple cache.
PoolCounter protected work wrapping RenderedRevision->getRevisionParserOutput.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
MediaWiki adaptation of StatsdDataFactory that provides buffering functionality.
exists()
Checks if the page currently exists.
getId( $wikiId=self::LOCAL)
Returns the page ID.