Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
76.00% covered (warning)
76.00%
38 / 50
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
RenderDebugInfo
76.00% covered (warning)
76.00%
38 / 50
75.00% covered (warning)
75.00%
3 / 4
23.99
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 shouldRun
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 transformText
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 debugInfo
73.91% covered (warning)
73.91%
34 / 46
0.00% covered (danger)
0.00%
0 / 1
20.54
1<?php
2
3namespace MediaWiki\OutputTransform\Stages;
4
5use MediaWiki\HookContainer\HookContainer;
6use MediaWiki\HookContainer\HookRunner;
7use MediaWiki\Language\RawMessage;
8use MediaWiki\Message\Message;
9use MediaWiki\OutputTransform\ContentTextTransformStage;
10use MediaWiki\Parser\ParserOutput;
11use ParserOptions;
12
13/**
14 * Adds debug info to the output
15 * @internal
16 */
17class RenderDebugInfo extends ContentTextTransformStage {
18
19    private HookRunner $hookRunner;
20
21    public function __construct( HookContainer $hookContainer ) {
22        $this->hookRunner = new HookRunner( $hookContainer );
23    }
24
25    public function shouldRun( ParserOutput $po, ?ParserOptions $popts, array $options = [] ): bool {
26        return $options['includeDebugInfo'] ?? false;
27    }
28
29    protected function transformText( string $text, ParserOutput $po, ?ParserOptions $popts, array &$options ): string {
30        $debugInfo = $this->debugInfo( $po );
31        return $text . $debugInfo;
32    }
33
34    private function debugInfo( ParserOutput $po ): string {
35        $text = '';
36
37        $limitReportData = $po->getLimitReportData();
38        // If nothing set it, we can't get it.
39        if ( $limitReportData ) {
40            $limitReport = "NewPP limit report\n";
41
42            if ( array_key_exists( 'cachereport-origin', $limitReportData ) ) {
43                $limitReport .= "Parsed by {$limitReportData['cachereport-origin']}\n";
44            }
45
46            if ( array_key_exists( 'cachereport-timestamp', $limitReportData ) ) {
47                $limitReport .= "Cached time: {$limitReportData['cachereport-timestamp']}\n";
48            }
49
50            if ( array_key_exists( 'cachereport-ttl', $limitReportData ) ) {
51                $limitReport .= "Cache expiry: {$limitReportData['cachereport-ttl']}\n";
52            }
53
54            if ( array_key_exists( 'cachereport-transientcontent', $limitReportData ) ) {
55                $transient = $limitReportData['cachereport-transientcontent'] ? 'true' : 'false';
56                $limitReport .= "Reduced expiry: $transient\n";
57            }
58
59            // TODO: flags should go into limit report too.
60            $limitReport .= 'Complications: [' . implode( ', ', $po->getAllFlags() ) . "]\n";
61
62            foreach ( $limitReportData as $key => $value ) {
63                if ( in_array( $key, [
64                    'cachereport-origin',
65                    'cachereport-timestamp',
66                    'cachereport-ttl',
67                    'cachereport-transientcontent',
68                    'limitreport-timingprofile',
69                ] ) ) {
70                    // These keys are processed separately.
71                    continue;
72                }
73
74                // TODO inject MessageFormatter instead of Message::newFromSpecifier
75                if ( $this->hookRunner->onParserLimitReportFormat(
76                    $key, $value, $limitReport, false, false )
77                ) {
78                    $keyMsg = Message::newFromSpecifier( $key )->inLanguage( 'en' )->useDatabase( false );
79                    $valueMsg = Message::newFallbackSequence( [ "$key-value-text", "$key-value" ] )
80                        ->inLanguage( 'en' )->useDatabase( false );
81                    if ( !$valueMsg->exists() ) {
82                        $valueMsg = new RawMessage( '$1' );
83                    }
84                    if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
85                        $valueMsg->params( $value );
86                        $limitReport .= "{$keyMsg->text()}{$valueMsg->text()}\n";
87                    }
88                }
89            }
90            // Since we're not really outputting HTML, decode the entities and
91            // then re-encode the things that need hiding inside HTML comments.
92            $limitReport = htmlspecialchars_decode( $limitReport );
93
94            // Sanitize for comment. Note '‐' in the replacement is U+2010,
95            // which looks much like the problematic '-'.
96            $limitReport = str_replace( [ '-', '&' ], [ '‐', '&amp;' ], $limitReport );
97            $text = "\n<!-- \n$limitReport-->\n";
98
99            $profileReport = $limitReportData['limitreport-timingprofile'] ?? null;
100            if ( $profileReport ) {
101                $text .= "<!--\nTransclusion expansion time report (%,ms,calls,template)\n";
102                $text .= implode( "\n", $profileReport ) . "\n-->\n";
103            }
104        }
105
106        if ( $po->getCacheMessage() ) {
107            $text .= "\n<!-- " . $po->getCacheMessage() . "\n -->\n";
108        }
109
110        $parsoidVersion = $po->getExtensionData( 'core:parsoid-version' );
111        if ( $parsoidVersion ) {
112            $text .= "\n<!--Parsoid $parsoidVersion-->\n";
113        }
114
115        return $text;
116    }
117}