MediaWiki fundraising/REL1_35
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
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 $isFast = false;
64
66 private $error = false;
67
79 $revid, $useParserCache, $revision = null, $audience = RevisionRecord::FOR_PUBLIC
80 ) {
81 if ( is_string( $revision ) ) { // BC: very old style call
82 $revRecord = $page->getRevisionRecord();
83 $mainSlot = $revRecord->getSlot( SlotRecord::MAIN, RevisionRecord::RAW );
84 $modelId = $mainSlot->getModel();
85 $format = $mainSlot->getFormat();
86
87 if ( $format === null ) {
88 $format = MediaWikiServices::getInstance()
89 ->getContentHandlerFactory()
90 ->getContentHandler( $modelId )
91 ->getDefaultFormat();
92 }
93
94 $revision = ContentHandler::makeContent( $revision, $page->getTitle(), $modelId, $format );
95 }
96
97 if ( $revision instanceof Content ) { // BC: old style call
100 $revision->setId( $revid );
101 $revision->setPageId( $page->getId() );
102 $revision->setContent( SlotRecord::MAIN, $content );
103 }
104
105 if ( $revision ) {
106 // Check that the RevisionRecord matches $revid and $page, but still allow
107 // fake RevisionRecords coming from errors or hooks in Article to be rendered.
108 if ( $revision->getId() && $revision->getId() !== $revid ) {
109 throw new InvalidArgumentException( '$revid parameter mismatches $revision parameter' );
110 }
111 if ( $revision->getPageId()
112 && $revision->getPageId() !== $page->getTitle()->getArticleID()
113 ) {
114 throw new InvalidArgumentException( '$page parameter mismatches $revision parameter' );
115 }
116 }
117
118 // TODO: DI: inject services
119 $this->renderer = MediaWikiServices::getInstance()->getRevisionRenderer();
120 $this->revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
121 $this->parserCache = MediaWikiServices::getInstance()->getParserCache();
122
123 $this->page = $page;
124 $this->revid = $revid;
125 $this->cacheable = $useParserCache;
126 $this->parserOptions = $parserOptions;
127 $this->revision = $revision;
128 $this->audience = $audience;
129 $this->cacheKey = $this->parserCache->getKey( $page, $parserOptions );
130 $keyPrefix = $this->cacheKey ?: ObjectCache::getLocalClusterInstance()->makeKey(
131 'articleview', 'missingcachekey'
132 );
133
134 parent::__construct( 'ArticleView', $keyPrefix . ':revid:' . $revid );
135 }
136
142 public function getParserOutput() {
143 return $this->parserOutput;
144 }
145
151 public function getIsDirty() {
152 return $this->isDirty;
153 }
154
160 public function getIsFastStale() {
161 return $this->isFast;
162 }
163
169 public function getError() {
170 return $this->error;
171 }
172
176 public function doWork() {
177 global $wgUseFileCache;
178
179 // @todo several of the methods called on $this->page are not declared in Page, but present
180 // in WikiPage and delegated by Article.
181
182 $isCurrent = $this->revid === $this->page->getLatest();
183
184 // The current revision cannot be hidden so we can skip some checks.
185 $audience = $isCurrent ? RevisionRecord::RAW : $this->audience;
186
187 if ( $this->revision !== null ) {
188 $rev = $this->revision;
189 } elseif ( $isCurrent ) {
190 $rev = $this->page->getRevisionRecord();
191 } else {
192 $rev = $this->revisionStore->getRevisionByTitle( $this->page->getTitle(), $this->revid );
193 }
194
195 if ( !$rev ) {
196 // couldn't load
197 return false;
198 }
199
200 $renderedRevision = $this->renderer->getRenderedRevision(
201 $rev,
202 $this->parserOptions,
203 null,
204 [ 'audience' => $audience ]
205 );
206
207 if ( !$renderedRevision ) {
208 // audience check failed
209 return false;
210 }
211
212 // Reduce effects of race conditions for slow parses (T48014)
213 $cacheTime = wfTimestampNow();
214
215 $time = -microtime( true );
216 $this->parserOutput = $renderedRevision->getRevisionParserOutput();
217 $time += microtime( true );
218
219 // Timing hack
220 if ( $time > 3 ) {
221 // TODO: Use Parser's logger (once it has one)
222 $logger = MediaWiki\Logger\LoggerFactory::getInstance( 'slow-parse' );
223 $logger->info( '{time} {title}', [
224 'time' => number_format( $time, 2 ),
225 'title' => $this->page->getTitle()->getPrefixedDBkey(),
226 'ns' => $this->page->getTitle()->getNamespace(),
227 'trigger' => 'view',
228 ] );
229 }
230
231 if ( $this->cacheable && $this->parserOutput->isCacheable() && $isCurrent ) {
232 $this->parserCache->save(
233 $this->parserOutput, $this->page, $this->parserOptions, $cacheTime, $this->revid );
234 }
235
236 // Make sure file cache is not used on uncacheable content.
237 // Output that has magic words in it can still use the parser cache
238 // (if enabled), though it will generally expire sooner.
239 if ( !$this->parserOutput->isCacheable() ) {
240 $wgUseFileCache = false;
241 }
242
243 if ( $isCurrent ) {
244 $this->page->triggerOpportunisticLinksUpdate( $this->parserOutput );
245 }
246
247 return true;
248 }
249
253 public function getCachedWork() {
254 $this->parserOutput = $this->parserCache->get( $this->page, $this->parserOptions );
255
256 if ( $this->parserOutput === false ) {
257 wfDebug( __METHOD__ . ": parser cache miss" );
258 return false;
259 } else {
260 wfDebug( __METHOD__ . ": parser cache hit" );
261 return true;
262 }
263 }
264
269 public function fallback( $fast ) {
270 $this->parserOutput = $this->parserCache->getDirty( $this->page, $this->parserOptions );
271
272 $fastMsg = '';
273 if ( $this->parserOutput && $fast ) {
274 /* Check if the stale response is from before the last write to the
275 * DB by this user. Declining to return a stale response in this
276 * case ensures that the user will see their own edit after page
277 * save.
278 *
279 * Note that the CP touch time is the timestamp of the shutdown of
280 * the save request, so there is a bias towards avoiding fast stale
281 * responses of potentially several seconds.
282 */
283 $lastWriteTime = MediaWikiServices::getInstance()->getDBLoadBalancerFactory()
284 ->getChronologyProtectorTouched();
285 $cacheTime = MWTimestamp::convert( TS_UNIX, $this->parserOutput->getCacheTime() );
286 if ( $lastWriteTime && $cacheTime <= $lastWriteTime ) {
287 wfDebugLog( 'dirty', "declining to send dirty output since cache time " .
288 $cacheTime . " is before last write time $lastWriteTime" );
289 // Forget this ParserOutput -- we will request it again if
290 // necessary in slow mode. There might be a newer entry
291 // available by that time.
292 $this->parserOutput = false;
293 return false;
294 }
295 $this->isFast = true;
296 $fastMsg = 'fast ';
297 }
298
299 if ( $this->parserOutput === false ) {
300 wfDebugLog( 'dirty', 'dirty missing' );
301 return false;
302 } else {
303 wfDebugLog( 'dirty', "{$fastMsg}dirty output {$this->cacheKey}" );
304 $this->isDirty = true;
305 return true;
306 }
307 }
308
313 public function error( $status ) {
314 $this->error = $status;
315 return false;
316 }
317}
$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.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Page revision base class.
The RevisionRenderer service provides access to rendered output for revisions.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
Set options of the Parser.
Class for dealing with PoolCounters using class members.
RevisionRenderer $renderer
getError()
Get a Status object in case of error or false otherwise.
ParserOutput bool $parserOutput
getParserOutput()
Get the ParserOutput from this object, or false in case of failure.
getIsDirty()
Get whether the ParserOutput is a dirty one (i.e.
getIsFastStale()
Get whether the ParserOutput was retrieved in fast stale mode.
RevisionRecord null $revision
__construct(WikiPage $page, ParserOptions $parserOptions, $revid, $useParserCache, $revision=null, $audience=RevisionRecord::FOR_PUBLIC)
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Class representing a MediaWiki article and history.
Definition WikiPage.php:51
getTitle()
Get the title object of the article.
Definition WikiPage.php:318
getRevisionRecord()
Get the latest revision.
Definition WikiPage.php:781
Base interface for content objects.
Definition Content.php:35
$content
Definition router.php:76