35 private $saveParseLogger;
38 private $loadBalancer;
41 private $roleRegistry;
44 private $contentRenderer;
61 $this->loadBalancer = $loadBalancer;
62 $this->roleRegistry = $roleRegistry;
63 $this->contentRenderer = $contentRenderer;
64 $this->dbDomain = $dbDomain;
65 $this->saveParseLogger =
new NullLogger();
69 public function setLogger( LoggerInterface $saveParseLogger ): void {
70 $this->saveParseLogger = $saveParseLogger;
106 if ( $rev->
getWikiId() !== $this->dbDomain ) {
107 throw new InvalidArgumentException(
108 "Mismatching wiki ID rev={$rev->getWikiId()}, this={$this->dbDomain}"
112 $audience = $hints[
'audience']
122 $options = $forPerformer ?
123 ParserOptions::newFromUser( $forPerformer->getUser() ) :
124 ParserOptions::newFromAnon();
127 if ( isset( $hints[
'causeAction'] ) ) {
128 $options->setRenderReason( $hints[
'causeAction'] );
131 $usePrimary = $hints[
'use-master'] ??
false;
133 $dbIndex = $usePrimary
137 $options->setSpeculativeRevIdCallback(
function () use ( $dbIndex ) {
138 return $this->getSpeculativeRevId( $dbIndex );
140 $options->setSpeculativePageIdCallback(
function () use ( $dbIndex ) {
141 return $this->getSpeculativePageId( $dbIndex );
151 $previousOutput = $hints[
'previous-output'] ??
null;
155 $this->contentRenderer,
156 function (
RenderedRevision $rrev, array $hints ) use ( $options, $previousOutput ) {
157 $h = [
'previous-output' => $previousOutput ] + $hints;
158 return $this->combineSlotOutput( $rrev, $options, $h );
166 if ( isset( $hints[
'known-revision-output'] ) ) {
167 $renderedRevision->setRevisionParserOutput( $hints[
'known-revision-output'] );
170 return $renderedRevision;
173 private function getSpeculativeRevId(
int $dbIndex ): int {
178 $db = $this->loadBalancer->
getConnection( $dbIndex, [], $this->dbDomain, $flags );
180 return 1 + (int)$db->newSelectQueryBuilder()
181 ->select(
'MAX(rev_id)' )
183 ->caller( __METHOD__ )->fetchField();
186 private function getSpeculativePageId(
int $dbIndex ): int {
191 $db = $this->loadBalancer->
getConnection( $dbIndex, [], $this->dbDomain, $flags );
193 return 1 + (int)$db->newSelectQueryBuilder()
194 ->select(
'MAX(page_id)' )
196 ->caller( __METHOD__ )->fetchField();
213 private function combineSlotOutput( RenderedRevision $rrev, ParserOptions $options, array $hints = [] ) {
214 $revision = $rrev->getRevision();
215 $slots = $revision->getSlots()->getSlots();
217 $withHtml = $hints[
'generate-html'] ??
true;
218 $previousOutputs = $this->splitSlotOutput( $rrev, $options, $hints[
'previous-output'] ??
null );
223 if ( array_keys( $slots ) === [ SlotRecord::MAIN ] || $options->getUseParsoid() ) {
224 $h = [
'previous-output' => $previousOutputs[SlotRecord::MAIN] ] + $hints;
225 return $rrev->getSlotParserOutput( SlotRecord::MAIN, $h );
229 if ( isset( $slots[SlotRecord::MAIN] ) ) {
230 $slots = [ SlotRecord::MAIN => $slots[SlotRecord::MAIN] ] + $slots;
233 $combinedOutput =
new ParserOutput(
null );
236 $options = $rrev->getOptions();
237 $options->registerWatcher( $combinedOutput->recordOption( ... ) );
239 foreach ( $slots as $role => $slot ) {
240 $h = [
'previous-output' => $previousOutputs[$role] ] + $hints;
241 $out = $rrev->getSlotParserOutput( $role, $h );
242 $slotOutput[$role] = $out;
245 $combinedOutput->mergeInternalMetaDataFrom( $out );
246 $combinedOutput->mergeTrackingMetaDataFrom( $out );
253 foreach ( $slotOutput as $role => $out ) {
254 $roleHandler = $this->roleRegistry->getRoleHandler( $role );
257 $layout = $roleHandler->getOutputLayoutHints();
258 $display = $layout[
'display'] ??
'section';
260 if ( $display ===
'none' ) {
269 $headText = Html::element(
'mw:slotheader', [], $role );
270 $html .= Html::rawElement(
'h1', [
'class' =>
'mw-slot-header' ], $headText );
275 $html .= $out->getRawText();
276 $combinedOutput->mergeHtmlMetaDataFrom( $out );
279 $combinedOutput->setRawText( $html );
282 $options->registerWatcher(
null );
283 return $combinedOutput;
303 private function splitSlotOutput( RenderedRevision $rrev, ParserOptions $options, ?ParserOutput $previousOutput ) {
305 $revision = $rrev->getRevision();
306 $revslots = $revision->getSlots();
307 if ( $previousOutput ===
null ) {
308 return array_fill_keys( $revslots->getSlotRoles(),
null );
314 if ( $revslots->getSlotRoles() === [ SlotRecord::MAIN ] || $options->getUseParsoid() ) {
315 return [ SlotRecord::MAIN => $previousOutput ];
319 return array_fill_keys( $revslots->getSlotRoles(),
null );