36 private $saveParseLogger;
39 private $loadBalancer;
42 private $roleRegistry;
45 private $contentRenderer;
62 $this->loadBalancer = $loadBalancer;
63 $this->roleRegistry = $roleRegistry;
64 $this->contentRenderer = $contentRenderer;
65 $this->dbDomain = $dbDomain;
66 $this->saveParseLogger =
new NullLogger();
70 public function setLogger( LoggerInterface $saveParseLogger ): void {
71 $this->saveParseLogger = $saveParseLogger;
107 if ( $rev->
getWikiId() !== $this->dbDomain ) {
108 throw new InvalidArgumentException(
109 "Mismatching wiki ID rev={$rev->getWikiId()}, this={$this->dbDomain}"
113 $audience = $hints[
'audience']
123 $options = $forPerformer ?
124 ParserOptions::newFromUser( $forPerformer->getUser() ) :
125 ParserOptions::newFromAnon();
128 if ( isset( $hints[
'causeAction'] ) ) {
129 $options->setRenderReason( $hints[
'causeAction'] );
132 $usePrimary = $hints[
'use-master'] ??
false;
134 $dbIndex = $usePrimary
138 $options->setSpeculativeRevIdCallback(
function () use ( $dbIndex ) {
139 return $this->getSpeculativeRevId( $dbIndex );
141 $options->setSpeculativePageIdCallback(
function () use ( $dbIndex ) {
142 return $this->getSpeculativePageId( $dbIndex );
152 $previousOutput = $hints[
'previous-output'] ??
null;
156 $this->contentRenderer,
157 function (
RenderedRevision $rrev, array $hints ) use ( $options, $previousOutput ) {
158 $h = [
'previous-output' => $previousOutput ] + $hints;
159 return $this->combineSlotOutput( $rrev, $options, $h );
167 if ( isset( $hints[
'known-revision-output'] ) ) {
168 $renderedRevision->setRevisionParserOutput( $hints[
'known-revision-output'] );
171 return $renderedRevision;
174 private function getSpeculativeRevId(
int $dbIndex ): int {
179 $db = $this->loadBalancer->
getConnection( $dbIndex, [], $this->dbDomain, $flags );
181 return 1 + (int)$db->newSelectQueryBuilder()
182 ->select(
'MAX(rev_id)' )
184 ->caller( __METHOD__ )->fetchField();
187 private function getSpeculativePageId(
int $dbIndex ): int {
192 $db = $this->loadBalancer->
getConnection( $dbIndex, [], $this->dbDomain, $flags );
194 return 1 + (int)$db->newSelectQueryBuilder()
195 ->select(
'MAX(page_id)' )
197 ->caller( __METHOD__ )->fetchField();
214 private function combineSlotOutput( RenderedRevision $rrev, ParserOptions $options, array $hints = [] ) {
215 $revision = $rrev->getRevision();
216 $slots = $revision->getSlots()->getSlots();
218 $withHtml = $hints[
'generate-html'] ??
true;
219 $previousOutputs = $this->splitSlotOutput( $rrev, $options, $hints[
'previous-output'] ??
null );
224 if ( array_keys( $slots ) === [ SlotRecord::MAIN ] || $options->getUseParsoid() ) {
225 $h = [
'previous-output' => $previousOutputs[SlotRecord::MAIN] ] + $hints;
226 return $rrev->getSlotParserOutput( SlotRecord::MAIN, $h );
230 if ( isset( $slots[SlotRecord::MAIN] ) ) {
231 $slots = [ SlotRecord::MAIN => $slots[SlotRecord::MAIN] ] + $slots;
234 $combinedOutput =
new ParserOutput(
null );
237 $options = $rrev->getOptions();
238 $options->registerWatcher( $combinedOutput->recordOption( ... ) );
240 foreach ( $slots as $role => $slot ) {
241 $h = [
'previous-output' => $previousOutputs[$role] ] + $hints;
242 $out = $rrev->getSlotParserOutput( $role, $h );
243 $slotOutput[$role] = $out;
247 $combinedOutput->mergeInternalMetaDataFrom( $out );
248 $combinedOutput->mergeTrackingMetaDataFrom( $out );
255 foreach ( $slotOutput as $role => $out ) {
256 $roleHandler = $this->roleRegistry->getRoleHandler( $role );
259 $layout = $roleHandler->getOutputLayoutHints();
260 $display = $layout[
'display'] ??
'section';
262 if ( $display ===
'none' ) {
271 $headText = Html::element(
'mw:slotheader', [], $role );
272 $html .= Html::rawElement(
'h1', [
'class' =>
'mw-slot-header' ], $headText );
273 $combinedOutput->setOutputFlag( ParserOutputFlags::HAS_SLOT_HEADERS );
278 $html .= $out->getContentHolderText();
279 $combinedOutput->mergeHtmlMetaDataFrom( $out );
282 $combinedOutput->setContentHolderText( $html );
285 $options->registerWatcher(
null );
286 return $combinedOutput;
306 private function splitSlotOutput( RenderedRevision $rrev, ParserOptions $options, ?ParserOutput $previousOutput ) {
308 $revision = $rrev->getRevision();
309 $revslots = $revision->getSlots();
310 if ( $previousOutput ===
null ) {
311 return array_fill_keys( $revslots->getSlotRoles(),
null );
317 if ( $revslots->getSlotRoles() === [ SlotRecord::MAIN ] || $options->getUseParsoid() ) {
318 return [ SlotRecord::MAIN => $previousOutput ];
322 return array_fill_keys( $revslots->getSlotRoles(),
null );