Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.59% covered (success)
94.59%
35 / 37
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
PageBundleParserOutputConverter
94.59% covered (success)
94.59%
35 / 37
60.00% covered (warning)
60.00%
3 / 5
8.01
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 parserOutputFromPageBundle
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 applyPageBundleDataToParserOutput
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
2
 pageBundleFromParserOutput
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
 hasPageBundle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Parser\Parsoid;
4
5use LanguageCode;
6use MediaWiki\Parser\ParserOutput;
7use Wikimedia\Parsoid\Core\PageBundle;
8
9/**
10 * Provides methods for conversion between PageBundle and ParserOutput
11 * TODO: Convert to a trait once we drop support for PHP < 8.2 since
12 * support for constants in traits was added in PHP 8.2
13 * @since 1.40
14 * @internal
15 */
16final class PageBundleParserOutputConverter {
17    /**
18     * @var string Key used to store parsoid page bundle data in ParserOutput
19     */
20    public const PARSOID_PAGE_BUNDLE_KEY = 'parsoid-page-bundle';
21
22    /**
23     * We do not want instances of this class to be created
24     * @return void
25     */
26    private function __construct() {
27    }
28
29    /**
30     * Creates a ParserOutput object containing the relevant data from
31     * the given PageBundle object.
32     *
33     * We need to inject data-parsoid and other properties into the
34     * parser output object for caching, so we can use it for VE edits
35     * and transformations.
36     *
37     * @param PageBundle $pageBundle
38     * @param ?ParserOutput $originalParserOutput Any non-parsoid metadata
39     *  from $originalParserOutput will be copied into the new ParserOutput object.
40     *
41     * @return ParserOutput
42     */
43    public static function parserOutputFromPageBundle(
44        PageBundle $pageBundle, ?ParserOutput $originalParserOutput = null
45    ): ParserOutput {
46        $parserOutput = new ParserOutput( $pageBundle->html );
47        if ( $originalParserOutput ) {
48            $parserOutput->mergeHtmlMetaDataFrom( $originalParserOutput );
49            $parserOutput->mergeTrackingMetaDataFrom( $originalParserOutput );
50            $parserOutput->mergeInternalMetaDataFrom( $originalParserOutput );
51        }
52        self::applyPageBundleDataToParserOutput( $pageBundle, $parserOutput );
53        return $parserOutput;
54    }
55
56    /**
57     * Given an existing ParserOutput and a PageBundle, applies the PageBundle data to the ParserOutput.
58     * NOTE: it does NOT apply the text of said pageBundle - this should be done by the calling method, if desired.
59     * This way, we can modify a ParserOutput's associated bundle without creating a new ParserOutput,
60     * which makes it easier to deal with in the OutputTransformPipeline.
61     * @param PageBundle|\stdClass $pageBundle
62     * @param ParserOutput $parserOutput
63     * @internal
64     */
65    public static function applyPageBundleDataToParserOutput(
66        $pageBundle, ParserOutput $parserOutput
67    ): void {
68        // Overwriting ExtensionData was deprecated in 1.38 but it's safe inside an OutputTransform pipeline,
69        // which is the only place where this should happen right now.
70        $parserOutput->setExtensionData(
71            self::PARSOID_PAGE_BUNDLE_KEY,
72            [
73                'parsoid' => $pageBundle->parsoid ?? null,
74                'mw' => $pageBundle->mw ?? null,
75                'version' => $pageBundle->version ?? null,
76                'headers' => $pageBundle->headers ?? null,
77                'contentmodel' => $pageBundle->contentmodel ?? null,
78            ]
79        );
80
81        if ( isset( $pageBundle->headers['content-language'] ) ) {
82            $lang = LanguageCode::normalizeNonstandardCodeAndWarn(
83            // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
84                $pageBundle->headers['content-language']
85            );
86            $parserOutput->setLanguage( $lang );
87        }
88    }
89
90    /**
91     * Returns a Parsoid PageBundle equivalent to the given ParserOutput.
92     *
93     * @param ParserOutput $parserOutput
94     *
95     * @return PageBundle
96     */
97    public static function pageBundleFromParserOutput( ParserOutput $parserOutput ): PageBundle {
98        $pageBundleData = $parserOutput->getExtensionData( self::PARSOID_PAGE_BUNDLE_KEY );
99        $lang = $parserOutput->getLanguage();
100
101        $headers = $pageBundleData['headers'] ?? [];
102
103        if ( $lang ) {
104            $headers['content-language'] = $lang->toBcp47Code();
105        }
106
107        return new PageBundle(
108            $parserOutput->getRawText(),
109            $pageBundleData['parsoid'] ?? [],
110            $pageBundleData['mw'] ?? [],
111            // It would be nice to have this be "null", but PageBundle::responseData()
112            // chocks on that: T325137.
113            $pageBundleData['version'] ?? '0.0.0',
114            $pageBundleData['headers'] ?? $headers,
115            $pageBundleData['contentmodel'] ?? null
116        );
117    }
118
119    public static function hasPageBundle( ParserOutput $parserOutput ) {
120        return $parserOutput->getExtensionData( self::PARSOID_PAGE_BUNDLE_KEY ) !== null;
121    }
122}