xref: /dokuwiki/inc/ErrorHandler.php (revision 7e0b27f2adf6886e1828a6d77152719228566d88)
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 = $intro . get_class($e) . ': ' . $e->getMessage();
61        self::logException($e);
62        msg(hsc($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", [gmdate('c'), get_class($e), $e->getFile() . ':' . $e->getLine(), $e->getMessage()]) . "\n";
98        return io_saveFile($conf['cachedir'] . '/_error.log', $log, true);
99    }
100
101    /**
102     * Checks the the stacktrace for plugin files
103     *
104     * @param \Throwable $e
105     * @return false|string
106     */
107    protected static function guessPlugin($e)
108    {
109        foreach ($e->getTrace() as $line) {
110            if (
111                isset($line['class']) &&
112                preg_match('/\w+?_plugin_(\w+)/', $line['class'], $match)
113            ) {
114                return $match[1];
115            }
116
117            if (
118                isset($line['file']) &&
119                preg_match('/lib\/plugins\/(\w+)\//', str_replace('\\', '/', $line['file']), $match)
120            ) {
121                return $match[1];
122            }
123        }
124
125        return false;
126    }
127}
128