63 private $performer =
null;
69 private $revisionOutput =
null;
75 private $slotsOutput = [];
81 private $combineOutput;
86 private $saveParseLogger;
91 private $contentRenderer;
113 callable $combineOutput,
117 $this->options = $options;
119 $this->setRevisionInternal( $revision );
121 $this->contentRenderer = $contentRenderer;
122 $this->combineOutput = $combineOutput;
123 $this->saveParseLogger =
new NullLogger();
126 throw new InvalidArgumentException(
127 'User must be specified when setting audience to FOR_THIS_USER'
131 $this->audience = $audience;
132 $this->performer = $performer;
139 $this->saveParseLogger = $saveParseLogger;
153 return $this->revision;
160 return $this->options;
174 $this->revisionOutput = $output;
194 $withHtml = $hints[
'generate-html'] ??
true;
196 if ( !$this->revisionOutput
197 || ( $withHtml && !$this->revisionOutput->hasText() )
199 $output = call_user_func( $this->combineOutput, $this, $hints );
201 Assert::postcondition(
203 'Callback did not return a ParserOutput object!'
206 $this->revisionOutput = $output;
209 return $this->revisionOutput;
224 $withHtml = $hints[
'generate-html'] ??
true;
226 if ( !isset( $this->slotsOutput[ $role ] )
227 || ( $withHtml && !$this->slotsOutput[ $role ]->hasText() )
229 $content = $this->revision->getContentOrThrow( $role, $this->audience, $this->performer );
232 $output = $this->getSlotParserOutputUncached( $content, $withHtml );
234 if ( $withHtml && !$output->hasText() ) {
235 throw new LogicException(
236 'HTML generation was requested, but '
237 . get_class( $content )
239 .
'ContentRenderer::getParserOutput() returns a ParserOutput with no text set.'
244 $this->options->registerWatcher(
null );
246 $this->slotsOutput[ $role ] = $output;
249 return $this->slotsOutput[$role];
258 private function getSlotParserOutputUncached(
Content $content, $withHtml ) {
259 return $this->contentRenderer->getParserOutput(
261 $this->revision->getPage(),
278 if ( $rev->
getId() === $this->revision->getId() ) {
282 if ( $this->revision->getId() ) {
283 throw new LogicException(
'RenderedRevision already has a revision with ID '
284 . $this->revision->getId() .
', can\'t update to revision with ID ' . $rev->
getId() );
287 if ( !$this->revision->getSlots()->hasSameContent( $rev->
getSlots() ) ) {
288 throw new LogicException(
'Cannot update to a revision with different content!' );
291 $this->setRevisionInternal( $rev );
293 $this->pruneRevisionSensitiveOutput(
294 $this->revision->getPageId(),
295 $this->revision->getId(),
296 $this->revision->getTimestamp()
313 private function pruneRevisionSensitiveOutput(
318 if ( $this->revisionOutput ) {
319 if ( $this->outputVariesOnRevisionMetaData(
320 $this->revisionOutput,
325 $this->revisionOutput =
null;
328 $this->saveParseLogger->debug( __METHOD__ .
": no prepared revision output" );
331 foreach ( $this->slotsOutput as $role => $output ) {
332 if ( $this->outputVariesOnRevisionMetaData(
338 unset( $this->slotsOutput[$role] );
346 private function setRevisionInternal( RevisionRecord $revision ) {
347 $this->revision = $revision;
379 if ( $this->revision->isReadyForInsertion() || !$this->revision->getId() ) {
380 $oldCallback = $this->options->getCurrentRevisionRecordCallback();
381 $this->options->setCurrentRevisionRecordCallback(
382 function ( PageReference $parserPage, $parser =
null ) use ( $oldCallback ) {
383 if ( $this->revision->getPage()->isSamePageAs( $parserPage ) ) {
384 return $this->revision;
386 return call_user_func( $oldCallback, $parserPage, $parser );
406 private function outputVariesOnRevisionMetaData(
407 ParserOutput $parserOutput,
412 $logger = $this->saveParseLogger;
413 $varyMsg = __METHOD__ .
": cannot use prepared output for '{title}'";
414 $context = [
'title' => (string)$this->revision->getPage() ];
416 if ( $parserOutput->getOutputFlag( ParserOutputFlags::VARY_REVISION ) ) {
418 $logger->info(
"$varyMsg (vary-revision)", $context );
421 $parserOutput->getOutputFlag( ParserOutputFlags::VARY_REVISION_ID )
422 && $actualRevId !==
false
423 && ( $actualRevId ===
true || $parserOutput->getSpeculativeRevIdUsed() !== $actualRevId )
425 $logger->info(
"$varyMsg (vary-revision-id and wrong ID)", $context );
428 $parserOutput->getOutputFlag( ParserOutputFlags::VARY_REVISION_TIMESTAMP )
429 && $actualRevTimestamp !==
false
430 && ( $actualRevTimestamp ===
true ||
431 $parserOutput->getRevisionTimestampUsed() !== $actualRevTimestamp )
433 $logger->info(
"$varyMsg (vary-revision-timestamp and wrong timestamp)", $context );
436 $parserOutput->getOutputFlag( ParserOutputFlags::VARY_PAGE_ID )
437 && $actualPageId !==
false
438 && ( $actualPageId ===
true || $parserOutput->getSpeculativePageIdUsed() !== $actualPageId )
440 $logger->info(
"$varyMsg (vary-page-id and wrong ID)", $context );
442 } elseif ( $parserOutput->getOutputFlag( ParserOutputFlags::VARY_REVISION_EXISTS ) ) {
446 $logger->info(
"$varyMsg (vary-revision-exists)", $context );
449 $parserOutput->getOutputFlag( ParserOutputFlags::VARY_REVISION_SHA1 ) &&
450 $parserOutput->getRevisionUsedSha1Base36() !== $this->revision->getSha1()
454 $logger->info(
"$varyMsg (vary-revision-sha1 with wrong SHA-1)", $context );
465 $logger->debug( __METHOD__ .
": reusing prepared output for '{title}'", $context );
Base interface for representing page content.