xref: /dokuwiki/inc/ErrorHandler.php (revision ffa84f81215ee805194333e08773b61f12689af9)
1<?php
2
3namespace dokuwiki;
4
5class ErrorHandler
6{
7
8    /**
9     * Register the default error handling
10     */
11    public static function register()
12    {
13        set_error_handler([ErrorHandler::class, 'errorConverter']);
14        if (!defined('DOKU_UNITTEST')) {
15            set_exception_handler([ErrorHandler::class, 'fatalException']);
16        }
17    }
18
19    /**
20     * Default Exception handler to show a nice user message before dieing
21     *
22     * The exception is logged to the error log
23     *
24     * @param \Throwable $e
25     */
26    public static function fatalException($e)
27    {
28        $plugin = self::guessPlugin($e);
29        $title = hsc(get_class($e) . ': ' . $e->getMessage());
30        $msg = 'An unforeseen error has occured. This is most likely a bug somewhere.';
31        if ($plugin) $msg .= ' It might be a problem in the ' . $plugin . ' plugin.';
32        $logged = self::logException($e)
33            ? 'More info has been written to the DokuWiki _error.log'
34            : $e->getFile() . ':' . $e->getLine();
35
36        echo <<<EOT
37<!DOCTYPE html>
38<html>
39<head><title>$title</title></head>
40<body style="font-family: Arial, sans-serif">
41    <div style="width:60%; margin: auto; background-color: #fcc;
42                border: 1px solid #faa; padding: 0.5em 1em;">
43        <h1 style="font-size: 120%">$title</h1>
44        <p>$msg</p>
45        <p>$logged</p>
46    </div>
47</body>
48</html>
49EOT;
50    }
51
52    /**
53     * Convenience method to display an error message for the given Exception
54     *
55     * @param \Throwable $e
56     * @param string $intro
57     */
58    public static function showExceptionMsg($e, $intro = 'Error!')
59    {
60        $msg = hsc($intro).'<br />' . hsc(get_class($e) . ': ' . $e->getMessage());
61        if(self::logException($e)) $msg .= '<br />More info is available in the _error.log';
62        msg($msg, -1);
63    }
64
65    /**
66     * Default error handler to convert old school warnings, notices, etc to exceptions
67     *
68     * You should not need to call this directly!
69     *
70     * @param int $errno
71     * @param string $errstr
72     * @param string $errfile
73     * @param int $errline
74     * @return bool
75     * @throws \ErrorException
76     */
77    public static function errorConverter($errno, $errstr, $errfile, $errline)
78    {
79        if (!(error_reporting() & $errno)) {
80            // This error code is not included in error_reporting, so let it fall
81            // through to the standard PHP error handler
82            return false;
83        }
84        throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
85    }
86
87    /**
88     * Log the given exception to the error log
89     *
90     * @param \Throwable $e
91     * @return bool false if the logging failed
92     */
93    public static function logException($e)
94    {
95        global $conf;
96
97        $log = join("\t", [
98                gmdate('c'),
99                get_class($e),
100                $e->getFile() . '(' . $e->getLine() . ')',
101                $e->getMessage(),
102            ]) . "\n";
103        $log .= $e->getTraceAsString() . "\n";
104        return io_saveFile($conf['cachedir'] . '/_error.log', $log, true);
105    }
106
107    /**
108     * Checks the the stacktrace for plugin files
109     *
110     * @param \Throwable $e
111     * @return false|string
112     */
113    protected static function guessPlugin($e)
114    {
115        foreach ($e->getTrace() as $line) {
116            if (
117                isset($line['class']) &&
118                preg_match('/\w+?_plugin_(\w+)/', $line['class'], $match)
119            ) {
120                return $match[1];
121            }
122
123            if (
124                isset($line['file']) &&
125                preg_match('/lib\/plugins\/(\w+)\//', str_replace('\\', '/', $line['file']), $match)
126            ) {
127                return $match[1];
128            }
129        }
130
131        return false;
132    }
133}
134