Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
47.22% covered (danger)
47.22%
17 / 36
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
HttpError
48.57% covered (danger)
48.57%
17 / 35
66.67% covered (warning)
66.67%
4 / 6
27.46
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 isLoggable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStatusCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 report
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 doLog
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 getHTML
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Exception;
8
9use MediaWiki\Logger\LoggerFactory;
10use MediaWiki\Message\Message;
11use MediaWiki\Request\ContentSecurityPolicy;
12use Wikimedia\Http\HttpStatus;
13
14/**
15 * Show an error that looks like an HTTP server error.
16 * Replacement for wfHttpError().
17 *
18 * @newable
19 * @stable to extend
20 * @since 1.19
21 * @ingroup Exception
22 */
23class HttpError extends MWException {
24    /** @var int */
25    private $httpCode;
26    /** @var string|Message|null */
27    private $header;
28    /** @var string|Message */
29    private $content;
30
31    /**
32     * @stable to call
33     * @param int $httpCode HTTP status code to send to the client
34     * @param string|Message $content Content of the message
35     * @param string|Message|null $header Content of the header (\<title\> and \<h1\>)
36     * @param-taint $content tainted
37     * @param-taint $header tainted
38     */
39    public function __construct( $httpCode, $content, $header = null ) {
40        parent::__construct( (string)$content );
41        $this->httpCode = (int)$httpCode;
42        $this->header = $header;
43        $this->content = $content;
44    }
45
46    /**
47     * We don't want the default exception logging as we got our own logging set
48     * up in self::report.
49     *
50     * @see MWException::isLoggable
51     *
52     * @since 1.24
53     * @return bool
54     */
55    public function isLoggable() {
56        return false;
57    }
58
59    /**
60     * Returns the HTTP status code supplied to the constructor.
61     *
62     * @return int
63     */
64    public function getStatusCode() {
65        return $this->httpCode;
66    }
67
68    /**
69     * Report and log the HTTP error.
70     * Sends the appropriate HTTP status code and outputs an
71     * HTML page with an error message.
72     */
73    public function report() {
74        $this->doLog();
75
76        HttpStatus::header( $this->httpCode );
77        header( 'Content-type: text/html; charset=utf-8' );
78        ContentSecurityPolicy::sendRestrictiveHeader();
79
80        print $this->getHTML();
81    }
82
83    private function doLog() {
84        $logger = LoggerFactory::getInstance( 'HttpError' );
85        $content = $this->content;
86
87        if ( $content instanceof Message ) {
88            $content = $content->text();
89        }
90
91        $context = [
92            'file' => $this->getFile(),
93            'line' => $this->getLine(),
94            'http_code' => $this->httpCode,
95        ];
96
97        $logMsg = "$content ({http_code}) from {file}:{line}";
98
99        if ( $this->getStatusCode() < 500 ) {
100            $logger->info( $logMsg, $context );
101        } else {
102            $logger->error( $logMsg, $context );
103        }
104    }
105
106    /**
107     * Returns HTML for reporting the HTTP error.
108     * This will be a minimal but complete HTML document.
109     *
110     * @return string HTML
111     */
112    public function getHTML() {
113        if ( $this->header === null ) {
114            $titleHtml = htmlspecialchars( HttpStatus::getMessage( $this->httpCode ) );
115        } elseif ( $this->header instanceof Message ) {
116            $titleHtml = $this->header->escaped();
117        } else {
118            $titleHtml = htmlspecialchars( $this->header );
119        }
120
121        if ( $this->content instanceof Message ) {
122            $contentHtml = $this->content->escaped();
123        } else {
124            $contentHtml = nl2br( htmlspecialchars( $this->content ) );
125        }
126
127        return "<!DOCTYPE html>\n" .
128        "<html><head><title>$titleHtml</title><meta name=\"color-scheme\" content=\"light dark\" /></head>\n" .
129        "<body><h1>$titleHtml</h1><p>$contentHtml</p></body></html>\n";
130    }
131}
132
133/** @deprecated class alias since 1.44 */
134class_alias( HttpError::class, 'HttpError' );