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->getContent( $role, $this->audience, $this->performer );
233 'Access to the content has been suppressed for this audience'
237 $output = $this->getSlotParserOutputUncached(
$content, $withHtml );
239 if ( $withHtml && !$output->hasText() ) {
240 throw new LogicException(
241 'HTML generation was requested, but '
244 .
'ContentRenderer::getParserOutput() returns a ParserOutput with no text set.'
249 $this->options->registerWatcher(
null );
252 $this->slotsOutput[ $role ] = $output;
255 return $this->slotsOutput[$role];
264 private function getSlotParserOutputUncached(
Content $content, $withHtml ) {
265 $parserOutput = $this->contentRenderer->getParserOutput(
267 $this->revision->getPage(),
268 $this->revision->getId(),
274 $parserOutput->
setTimestamp( $this->revision->getTimestamp() );
275 return $parserOutput;
288 if ( $rev->
getId() === $this->revision->getId() ) {
292 if ( $this->revision->getId() ) {
293 throw new LogicException(
'RenderedRevision already has a revision with ID '
294 . $this->revision->getId() .
', can\'t update to revision with ID ' . $rev->
getId() );
297 if ( !$this->revision->getSlots()->hasSameContent( $rev->
getSlots() ) ) {
298 throw new LogicException(
'Cannot update to a revision with different content!' );
301 $this->setRevisionInternal( $rev );
303 $this->pruneRevisionSensitiveOutput(
304 $this->revision->getPageId(),
305 $this->revision->getId(),
306 $this->revision->getTimestamp()
323 private function pruneRevisionSensitiveOutput(
328 if ( $this->revisionOutput ) {
329 if ( $this->outputVariesOnRevisionMetaData(
330 $this->revisionOutput,
335 $this->revisionOutput =
null;
338 $this->saveParseLogger->debug( __METHOD__ .
": no prepared revision output" );
341 foreach ( $this->slotsOutput as $role => $output ) {
342 if ( $this->outputVariesOnRevisionMetaData(
348 unset( $this->slotsOutput[$role] );
356 private function setRevisionInternal( RevisionRecord $revision ) {
357 $this->revision = $revision;
389 if ( $this->revision->isReadyForInsertion() || !$this->revision->getId() ) {
390 $oldCallback = $this->options->getCurrentRevisionRecordCallback();
391 $this->options->setCurrentRevisionRecordCallback(
392 function ( PageReference $parserPage, $parser =
null ) use ( $oldCallback ) {
393 if ( $this->revision->getPage()->isSamePageAs( $parserPage ) ) {
394 return $this->revision;
396 return call_user_func( $oldCallback, $parserPage, $parser );
416 private function outputVariesOnRevisionMetaData(
422 $logger = $this->saveParseLogger;
423 $varyMsg = __METHOD__ .
": cannot use prepared output for '{title}'";
424 $context = [
'title' => (string)$this->revision->getPage() ];
426 if ( $parserOutput->
getOutputFlag( ParserOutputFlags::VARY_REVISION ) ) {
428 $logger->info(
"$varyMsg (vary-revision)", $context );
431 $parserOutput->
getOutputFlag( ParserOutputFlags::VARY_REVISION_ID )
432 && $actualRevId !==
false
435 $logger->info(
"$varyMsg (vary-revision-id and wrong ID)", $context );
438 $parserOutput->
getOutputFlag( ParserOutputFlags::VARY_REVISION_TIMESTAMP )
439 && $actualRevTimestamp !==
false
440 && ( $actualRevTimestamp ===
true ||
443 $logger->info(
"$varyMsg (vary-revision-timestamp and wrong timestamp)", $context );
446 $parserOutput->
getOutputFlag( ParserOutputFlags::VARY_PAGE_ID )
447 && $actualPageId !==
false
450 $logger->info(
"$varyMsg (vary-page-id and wrong ID)", $context );
452 } elseif ( $parserOutput->
getOutputFlag( ParserOutputFlags::VARY_REVISION_EXISTS ) ) {
456 $logger->info(
"$varyMsg (vary-revision-exists)", $context );
459 $parserOutput->
getOutputFlag( ParserOutputFlags::VARY_REVISION_SHA1 ) &&
464 $logger->info(
"$varyMsg (vary-revision-sha1 with wrong SHA-1)", $context );
475 $logger->debug( __METHOD__ .
": reusing prepared output for '{title}'", $context );
Base interface for content objects.