MediaWiki  master
PoolWorkArticleView.php
Go to the documentation of this file.
1 <?php
27 
30  private $page;
31 
33  private $cacheKey;
34 
36  private $revid;
37 
39  private $parserCache;
40 
42  private $parserOptions;
43 
45  private $revision = null;
46 
48  private $audience;
49 
51  private $revisionStore = null;
52 
54  private $renderer = null;
55 
57  private $parserOutput = false;
58 
60  private $isDirty = false;
61 
63  private $error = false;
64 
76  $revid, $useParserCache, $revision = null, $audience = RevisionRecord::FOR_PUBLIC
77  ) {
78  if ( is_string( $revision ) ) { // BC: very old style call
79  $modelId = $page->getRevision()->getContentModel();
80  $format = $page->getRevision()->getContentFormat();
81  $revision = ContentHandler::makeContent( $revision, $page->getTitle(), $modelId, $format );
82  }
83 
84  if ( $revision instanceof Content ) { // BC: old style call
86  $revision = new MutableRevisionRecord( $page->getTitle() );
87  $revision->setId( $revid );
88  $revision->setPageId( $page->getId() );
89  $revision->setContent( SlotRecord::MAIN, $content );
90  }
91 
92  if ( $revision ) {
93  // Check that the RevisionRecord matches $revid and $page, but still allow
94  // fake RevisionRecords coming from errors or hooks in Article to be rendered.
95  if ( $revision->getId() && $revision->getId() !== $revid ) {
96  throw new InvalidArgumentException( '$revid parameter mismatches $revision parameter' );
97  }
98  if ( $revision->getPageId()
99  && $revision->getPageId() !== $page->getTitle()->getArticleID()
100  ) {
101  throw new InvalidArgumentException( '$page parameter mismatches $revision parameter' );
102  }
103  }
104 
105  // TODO: DI: inject services
106  $this->renderer = MediaWikiServices::getInstance()->getRevisionRenderer();
107  $this->revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
108  $this->parserCache = MediaWikiServices::getInstance()->getParserCache();
109 
110  $this->page = $page;
111  $this->revid = $revid;
112  $this->cacheable = $useParserCache;
113  $this->parserOptions = $parserOptions;
114  $this->revision = $revision;
115  $this->audience = $audience;
116  $this->cacheKey = $this->parserCache->getKey( $page, $parserOptions );
117  $keyPrefix = $this->cacheKey ?: ObjectCache::getLocalClusterInstance()->makeKey(
118  'articleview', 'missingcachekey'
119  );
120 
121  parent::__construct( 'ArticleView', $keyPrefix . ':revid:' . $revid );
122  }
123 
129  public function getParserOutput() {
130  return $this->parserOutput;
131  }
132 
138  public function getIsDirty() {
139  return $this->isDirty;
140  }
141 
147  public function getError() {
148  return $this->error;
149  }
150 
154  public function doWork() {
155  global $wgUseFileCache;
156 
157  // @todo several of the methods called on $this->page are not declared in Page, but present
158  // in WikiPage and delegated by Article.
159 
160  $isCurrent = $this->revid === $this->page->getLatest();
161 
162  // The current revision cannot be hidden so we can skip some checks.
163  $audience = $isCurrent ? RevisionRecord::RAW : $this->audience;
164 
165  if ( $this->revision !== null ) {
166  $rev = $this->revision;
167  } elseif ( $isCurrent ) {
168  $rev = $this->page->getRevision()
169  ? $this->page->getRevision()->getRevisionRecord()
170  : null;
171  } else {
172  $rev = $this->revisionStore->getRevisionByTitle( $this->page->getTitle(), $this->revid );
173  }
174 
175  if ( !$rev ) {
176  // couldn't load
177  return false;
178  }
179 
180  $renderedRevision = $this->renderer->getRenderedRevision(
181  $rev,
182  $this->parserOptions,
183  null,
184  [ 'audience' => $audience ]
185  );
186 
187  if ( !$renderedRevision ) {
188  // audience check failed
189  return false;
190  }
191 
192  // Reduce effects of race conditions for slow parses (T48014)
193  $cacheTime = wfTimestampNow();
194 
195  $time = - microtime( true );
196  $this->parserOutput = $renderedRevision->getRevisionParserOutput();
197  $time += microtime( true );
198 
199  // Timing hack
200  if ( $time > 3 ) {
201  // TODO: Use Parser's logger (once it has one)
202  $logger = MediaWiki\Logger\LoggerFactory::getInstance( 'slow-parse' );
203  $logger->info( '{time} {title}', [
204  'time' => number_format( $time, 2 ),
205  'title' => $this->page->getTitle()->getPrefixedDBkey(),
206  'ns' => $this->page->getTitle()->getNamespace(),
207  'trigger' => 'view',
208  ] );
209  }
210 
211  if ( $this->cacheable && $this->parserOutput->isCacheable() && $isCurrent ) {
212  $this->parserCache->save(
213  $this->parserOutput, $this->page, $this->parserOptions, $cacheTime, $this->revid );
214  }
215 
216  // Make sure file cache is not used on uncacheable content.
217  // Output that has magic words in it can still use the parser cache
218  // (if enabled), though it will generally expire sooner.
219  if ( !$this->parserOutput->isCacheable() ) {
220  $wgUseFileCache = false;
221  }
222 
223  if ( $isCurrent ) {
224  $this->page->triggerOpportunisticLinksUpdate( $this->parserOutput );
225  }
226 
227  return true;
228  }
229 
233  public function getCachedWork() {
234  $this->parserOutput = $this->parserCache->get( $this->page, $this->parserOptions );
235 
236  if ( $this->parserOutput === false ) {
237  wfDebug( __METHOD__ . ": parser cache miss\n" );
238  return false;
239  } else {
240  wfDebug( __METHOD__ . ": parser cache hit\n" );
241  return true;
242  }
243  }
244 
248  public function fallback() {
249  $this->parserOutput = $this->parserCache->getDirty( $this->page, $this->parserOptions );
250 
251  if ( $this->parserOutput === false ) {
252  wfDebugLog( 'dirty', 'dirty missing' );
253  wfDebug( __METHOD__ . ": no dirty cache\n" );
254  return false;
255  } else {
256  wfDebug( __METHOD__ . ": sending dirty output\n" );
257  wfDebugLog( 'dirty', "dirty output {$this->cacheKey}" );
258  $this->isDirty = true;
259  return true;
260  }
261  }
262 
267  public function error( $status ) {
268  $this->error = $status;
269  return false;
270  }
271 }
ParserOutput bool $parserOutput
RevisionRecord null $revision
static getLocalClusterInstance()
Get the main cluster-local cache object.
static getInstance( $channel)
Get a named logger instance from the currently configured logger factory.
RevisionStore $revisionStore
getIsDirty()
Get whether the ParserOutput is a dirty one (i.e.
getParserOutput()
Get the ParserOutput from this object, or false in case of failure.
getRevision()
Get the latest revision.
Definition: WikiPage.php:787
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites...
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
getTitle()
Get the title object of the article.
Definition: WikiPage.php:298
getId()
Get revision ID.
RevisionRenderer $renderer
getError()
Get a Status object in case of error or false otherwise.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not...
ParserOptions $parserOptions
__construct(WikiPage $page, ParserOptions $parserOptions, $revid, $useParserCache, $revision=null, $audience=RevisionRecord::FOR_PUBLIC)
$content
Definition: router.php:78
Class for dealing with PoolCounters using class members.
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
getPageId()
Get the page ID.