Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.75% covered (success)
93.75%
30 / 32
87.50% covered (warning)
87.50%
7 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ErrorReporter
93.75% covered (success)
93.75%
30 / 32
87.50% covered (warning)
87.50%
7 / 8
11.03
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
 firstError
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 halfParsed
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 plain
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 msg
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 getInterfaceLanguageAndSplitCache
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 wrapInHtmlContainer
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
 parseTypeAndIdFromMessageKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Cite;
4
5use Language;
6use MediaWiki\Html\Html;
7use MediaWiki\Parser\Sanitizer;
8use Message;
9use Parser;
10use StatusValue;
11
12/**
13 * @license GPL-2.0-or-later
14 */
15class ErrorReporter {
16
17    private ReferenceMessageLocalizer $messageLocalizer;
18    private ?Language $cachedInterfaceLanguage = null;
19
20    public function __construct( ReferenceMessageLocalizer $messageLocalizer ) {
21        $this->messageLocalizer = $messageLocalizer;
22    }
23
24    /**
25     * @deprecated Intermediate helper function. Long-term all errors should be rendered, not only
26     * the first one.
27     */
28    public function firstError( Parser $parser, StatusValue $status ): string {
29        $error = $status->getErrors()[0];
30        return $this->halfParsed( $parser, $error['message'], ...$error['params'] );
31    }
32
33    /**
34     * @param Parser $parser
35     * @param string $key Message name of the error or warning
36     * @param mixed ...$params
37     *
38     * @return string Half-parsed wikitext with extension's tags already being expanded
39     */
40    public function halfParsed( Parser $parser, string $key, ...$params ): string {
41        $msg = $this->msg( $parser, $key, ...$params );
42        $wikitext = $parser->recursiveTagParse( $msg->plain() );
43        return $this->wrapInHtmlContainer( $wikitext, $key, $msg->getLanguage() );
44    }
45
46    /**
47     * @param Parser $parser
48     * @param string $key Message name of the error or warning
49     * @param mixed ...$params
50     *
51     * @return string Plain, unparsed wikitext
52     * @return-taint tainted
53     */
54    public function plain( Parser $parser, string $key, ...$params ): string {
55        $msg = $this->msg( $parser, $key, ...$params );
56        $wikitext = $msg->plain();
57        return $this->wrapInHtmlContainer( $wikitext, $key, $msg->getLanguage() );
58    }
59
60    /**
61     * @param Parser $parser
62     * @param string $key
63     * @param mixed ...$params
64     *
65     * @return Message
66     */
67    private function msg( Parser $parser, string $key, ...$params ): Message {
68        $language = $this->getInterfaceLanguageAndSplitCache( $parser );
69        $msg = $this->messageLocalizer->msg( $key, ...$params )->inLanguage( $language );
70
71        [ $type ] = $this->parseTypeAndIdFromMessageKey( $msg->getKey() );
72
73        if ( $type === 'error' ) {
74            // Take care; this is a sideeffect that might not belong to this class.
75            $parser->addTrackingCategory( 'cite-tracking-category-cite-error' );
76        }
77
78        // Messages: cite_error, cite_warning
79        return $this->messageLocalizer->msg( "cite_$type", $msg->plain() )->inLanguage( $language );
80    }
81
82    /**
83     * Note the startling side effect of splitting ParserCache by user interface language!
84     */
85    private function getInterfaceLanguageAndSplitCache( Parser $parser ): Language {
86        if ( !$this->cachedInterfaceLanguage ) {
87            $this->cachedInterfaceLanguage = $parser->getOptions()->getUserLangObj();
88        }
89        return $this->cachedInterfaceLanguage;
90    }
91
92    private function wrapInHtmlContainer(
93        string $wikitext,
94        string $key,
95        Language $language
96    ): string {
97        [ $type, $id ] = $this->parseTypeAndIdFromMessageKey( $key );
98        $extraClass = $type === 'warning'
99            ? ' mw-ext-cite-warning-' . Sanitizer::escapeClass( $id )
100            : '';
101
102        return Html::rawElement(
103            'span',
104            [
105                // The following classes are generated here:
106                // * mw-ext-cite-warning
107                // * mw-ext-cite-error
108                'class' => "$type mw-ext-cite-$type" . $extraClass,
109                'lang' => $language->getHtmlCode(),
110                'dir' => $language->getDir(),
111            ],
112            $wikitext
113        );
114    }
115
116    /**
117     * @param string $messageKey Expected to be a message key like "cite_error_ref_numeric_key"
118     *
119     * @return string[] Two elements, e.g. "error" and "ref_too_many_keys"
120     */
121    private function parseTypeAndIdFromMessageKey( string $messageKey ): array {
122        return array_slice( explode( '_', str_replace( '-', '_', $messageKey ), 3 ), 1 );
123    }
124
125}