getRevision()->getContentModel(); $format = $page->getRevision()->getContentFormat(); $content = ContentHandler::makeContent( $content, $page->getTitle(), $modelId, $format ); } $this->page = $page; $this->revid = $revid; $this->cacheable = $useParserCache; $this->parserOptions = $parserOptions; $this->content = $content; $this->parserCache = MediaWikiServices::getInstance()->getParserCache(); $this->cacheKey = $this->parserCache->getKey( $page, $parserOptions ); $keyPrefix = $this->cacheKey ?: wfMemcKey( 'articleview', 'missingcachekey' ); parent::__construct( 'ArticleView', $keyPrefix . ':revid:' . $revid ); } /** * Get the ParserOutput from this object, or false in case of failure * * @return ParserOutput|bool */ public function getParserOutput() { return $this->parserOutput; } /** * Get whether the ParserOutput is a dirty one (i.e. expired) * * @return bool */ public function getIsDirty() { return $this->isDirty; } /** * Get a Status object in case of error or false otherwise * * @return Status|bool */ public function getError() { return $this->error; } /** * @return bool */ public function doWork() { global $wgUseFileCache; // @todo several of the methods called on $this->page are not declared in Page, but present // in WikiPage and delegated by Article. $isCurrent = $this->revid === $this->page->getLatest(); if ( $this->content !== null ) { $content = $this->content; } elseif ( $isCurrent ) { // XXX: why use RAW audience here, and PUBLIC (default) below? $content = $this->page->getContent( Revision::RAW ); } else { $rev = Revision::newFromTitle( $this->page->getTitle(), $this->revid ); if ( $rev === null ) { $content = null; } else { // XXX: why use PUBLIC audience here (default), and RAW above? $content = $rev->getContent(); } } if ( $content === null ) { return false; } // Reduce effects of race conditions for slow parses (T48014) $cacheTime = wfTimestampNow(); $time = - microtime( true ); $this->parserOutput = $content->getParserOutput( $this->page->getTitle(), $this->revid, $this->parserOptions ); $time += microtime( true ); // Timing hack if ( $time > 3 ) { // TODO: Use Parser's logger (once it has one) $logger = MediaWiki\Logger\LoggerFactory::getInstance( 'slow-parse' ); $logger->info( '{time} {title}', [ 'time' => number_format( $time, 2 ), 'title' => $this->page->getTitle()->getPrefixedDBkey(), 'ns' => $this->page->getTitle()->getNamespace(), 'trigger' => 'view', ] ); } if ( $this->cacheable && $this->parserOutput->isCacheable() && $isCurrent ) { $this->parserCache->save( $this->parserOutput, $this->page, $this->parserOptions, $cacheTime, $this->revid ); } // Make sure file cache is not used on uncacheable content. // Output that has magic words in it can still use the parser cache // (if enabled), though it will generally expire sooner. if ( !$this->parserOutput->isCacheable() ) { $wgUseFileCache = false; } if ( $isCurrent ) { $this->page->triggerOpportunisticLinksUpdate( $this->parserOutput ); } return true; } /** * @return bool */ public function getCachedWork() { $this->parserOutput = $this->parserCache->get( $this->page, $this->parserOptions ); if ( $this->parserOutput === false ) { wfDebug( __METHOD__ . ": parser cache miss\n" ); return false; } else { wfDebug( __METHOD__ . ": parser cache hit\n" ); return true; } } /** * @return bool */ public function fallback() { $this->parserOutput = $this->parserCache->getDirty( $this->page, $this->parserOptions ); if ( $this->parserOutput === false ) { wfDebugLog( 'dirty', 'dirty missing' ); wfDebug( __METHOD__ . ": no dirty cache\n" ); return false; } else { wfDebug( __METHOD__ . ": sending dirty output\n" ); wfDebugLog( 'dirty', "dirty output {$this->cacheKey}" ); $this->isDirty = true; return true; } } /** * @param Status $status * @return bool */ public function error( $status ) { $this->error = $status; return false; } }