Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
MissingExtensionException
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 5
210
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 renderHtml
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
20
 renderText
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 render
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getMWLogo
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2use MediaWiki\Html\TemplateParser;
3
4/**
5 * Thrown when ExtensionRegistry cannot open the extension.json or skin.json file.
6 *
7 * We handle this case specially, because it is one of the more
8 * common errors a new MW sysadmin is likely to encounter and we
9 * want their initial experience to be good. wfLoadExtension()
10 * generally happens before MWExceptionRenderer gets installed
11 * so we cannot use that.
12 *
13 * @ingroup ExtensionRegistry
14 * @internal
15 */
16class MissingExtensionException extends Exception {
17    private bool $isSkin;
18    private string $extName = 'unknown';
19    private string $path;
20    private string $error;
21
22    /**
23     * @param string $path Path of file that cannot be read
24     * @param string $error Text of error mtime gave
25     */
26    public function __construct( string $path, string $error ) {
27        $this->isSkin = str_ends_with( $path, "/skin.json" );
28        $m = [];
29        preg_match( "!/([^/]*)/[^/]*.json$!", $path, $m );
30        if ( $m ) {
31            $this->extName = $m[1];
32        }
33        $this->path = $path;
34        $this->error = $error;
35
36        parent::__construct( "Error Loading extension. Unable to open file $path$error" );
37    }
38
39    /**
40     * Output error message as html.
41     *
42     * Avoid relying on MW stuff, as it might not be setup yet.
43     * We don't bother translating, as the user may not have even set lang yet.
44     *
45     */
46    private function renderHtml() {
47        if ( !headers_sent() ) {
48            HttpStatus::header( 500 );
49            header( 'Content-Type: text/html; charset=UTF-8' );
50        }
51
52        $templateParser = new TemplateParser( null, new EmptyBagOStuff() );
53
54        try {
55            echo $templateParser->processTemplate(
56                'ExtensionConfigError',
57                [
58                    'version' => MW_VERSION,
59                    'path' => $this->path,
60                    'type' => $this->isSkin ? 'skin' : 'extension',
61                    'error' => $this->error,
62                    'extName' => $this->extName,
63                    'trace' => $this->getTraceAsString(),
64                    'mwLogo' => $this->getMWLogo(),
65                ]
66            );
67        } catch ( Exception $e ) {
68            echo 'Error: ' . htmlspecialchars( $e->getMessage() );
69        }
70    }
71
72    /**
73     * Render the error for CLI
74     */
75    private function renderText() {
76        $type = $this->isSkin ? 'skin' : 'extension';
77        echo "Error: The $this->extName $type cannot be loaded. "
78            . "Check that all of its files are installed properly.\n\n";
79        echo $this->getTraceAsString();
80        echo "\n";
81    }
82
83    /**
84     * Output an error response and exit.
85     *
86     * @return never
87     */
88    public function render() {
89        if ( wfIsCli() ) {
90            $this->renderText();
91        } else {
92            $this->renderHtml();
93        }
94        // Make sure that the error gets into logs.
95        // This will also stop execution.
96        trigger_error( $this->getMessage(), E_USER_ERROR );
97    }
98
99    /**
100     * Get the url for the MW logo
101     *
102     * @return string
103     */
104    private function getMWLogo() {
105        global $wgResourceBasePath;
106        $suffix = "/resources/assets/mediawiki.png";
107        if ( $wgResourceBasePath !== null ) {
108            // We are early in setup, so we can't rely on this.
109            return $wgResourceBasePath . $suffix;
110        }
111        $path = '/';
112        foreach ( array_filter( explode( '/', $_SERVER['PHP_SELF'] ) ) as $part ) {
113            if ( !preg_match( '/\.php$/', $part ) ) {
114                $path .= "$part/";
115            } else {
116                break;
117            }
118        }
119
120        return $path . $suffix;
121    }
122}