xref: /dokuwiki/inc/ErrorHandler.php (revision 7e0b27f2adf6886e1828a6d77152719228566d88)
1642e976cSAndreas Gohr<?php
2642e976cSAndreas Gohr
3642e976cSAndreas Gohrnamespace dokuwiki;
4642e976cSAndreas Gohr
5642e976cSAndreas Gohrclass ErrorHandler
6642e976cSAndreas Gohr{
7642e976cSAndreas Gohr
8642e976cSAndreas Gohr    /**
9642e976cSAndreas Gohr     * Register the default error handling
10642e976cSAndreas Gohr     */
11642e976cSAndreas Gohr    public static function register()
12642e976cSAndreas Gohr    {
13642e976cSAndreas Gohr        set_error_handler([ErrorHandler::class, 'errorConverter']);
14642e976cSAndreas Gohr        if (!defined('DOKU_UNITTEST')) {
15642e976cSAndreas Gohr            set_exception_handler([ErrorHandler::class, 'fatalException']);
16642e976cSAndreas Gohr        }
17642e976cSAndreas Gohr    }
18642e976cSAndreas Gohr
19642e976cSAndreas Gohr    /**
20642e976cSAndreas Gohr     * Default Exception handler to show a nice user message before dieing
21642e976cSAndreas Gohr     *
22642e976cSAndreas Gohr     * The exception is logged to the error log
23642e976cSAndreas Gohr     *
24642e976cSAndreas Gohr     * @param \Throwable $e
25642e976cSAndreas Gohr     */
26642e976cSAndreas Gohr    public static function fatalException($e)
27642e976cSAndreas Gohr    {
28*7e0b27f2SAndreas Gohr        $plugin = self::guessPlugin($e);
29642e976cSAndreas Gohr        $title = hsc(get_class($e) . ': ' . $e->getMessage());
30642e976cSAndreas Gohr        $msg = 'An unforeseen error has occured. This is most likely a bug somewhere.';
31*7e0b27f2SAndreas Gohr        if ($plugin) $msg .= ' It might be a problem in the ' . $plugin . ' plugin.';
32642e976cSAndreas Gohr        $logged = self::logException($e)
33642e976cSAndreas Gohr            ? 'More info has been written to the DokuWiki _error.log'
34642e976cSAndreas Gohr            : $e->getFile() . ':' . $e->getLine();
35642e976cSAndreas Gohr
36642e976cSAndreas Gohr        echo <<<EOT
37642e976cSAndreas Gohr<!DOCTYPE html>
38642e976cSAndreas Gohr<html>
39642e976cSAndreas Gohr<head><title>$title</title></head>
40642e976cSAndreas Gohr<body style="font-family: Arial, sans-serif">
41642e976cSAndreas Gohr    <div style="width:60%; margin: auto; background-color: #fcc;
42642e976cSAndreas Gohr                border: 1px solid #faa; padding: 0.5em 1em;">
43642e976cSAndreas Gohr        <h1 style="font-size: 120%">$title</h1>
44642e976cSAndreas Gohr        <p>$msg</p>
45642e976cSAndreas Gohr        <p>$logged</p>
46642e976cSAndreas Gohr    </div>
47642e976cSAndreas Gohr</body>
48642e976cSAndreas Gohr</html>
49642e976cSAndreas GohrEOT;
50642e976cSAndreas Gohr    }
51642e976cSAndreas Gohr
52642e976cSAndreas Gohr    /**
53642e976cSAndreas Gohr     * Convenience method to display an error message for the given Exception
54642e976cSAndreas Gohr     *
55642e976cSAndreas Gohr     * @param \Throwable $e
56642e976cSAndreas Gohr     * @param string $intro
57642e976cSAndreas Gohr     */
58642e976cSAndreas Gohr    public static function showExceptionMsg($e, $intro = 'Error!')
59642e976cSAndreas Gohr    {
60642e976cSAndreas Gohr        $msg = $intro . get_class($e) . ': ' . $e->getMessage();
61642e976cSAndreas Gohr        self::logException($e);
62642e976cSAndreas Gohr        msg(hsc($msg), -1);
63642e976cSAndreas Gohr    }
64642e976cSAndreas Gohr
65642e976cSAndreas Gohr    /**
66642e976cSAndreas Gohr     * Default error handler to convert old school warnings, notices, etc to exceptions
67642e976cSAndreas Gohr     *
68642e976cSAndreas Gohr     * You should not need to call this directly!
69642e976cSAndreas Gohr     *
70642e976cSAndreas Gohr     * @param int $errno
71642e976cSAndreas Gohr     * @param string $errstr
72642e976cSAndreas Gohr     * @param string $errfile
73642e976cSAndreas Gohr     * @param int $errline
74642e976cSAndreas Gohr     * @return bool
75642e976cSAndreas Gohr     * @throws \ErrorException
76642e976cSAndreas Gohr     */
77642e976cSAndreas Gohr    public static function errorConverter($errno, $errstr, $errfile, $errline)
78642e976cSAndreas Gohr    {
79642e976cSAndreas Gohr        if (!(error_reporting() & $errno)) {
80642e976cSAndreas Gohr            // This error code is not included in error_reporting, so let it fall
81642e976cSAndreas Gohr            // through to the standard PHP error handler
82642e976cSAndreas Gohr            return false;
83642e976cSAndreas Gohr        }
84642e976cSAndreas Gohr        throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
85642e976cSAndreas Gohr    }
86642e976cSAndreas Gohr
87642e976cSAndreas Gohr    /**
88642e976cSAndreas Gohr     * Log the given exception to the error log
89642e976cSAndreas Gohr     *
90642e976cSAndreas Gohr     * @param \Throwable $e
91642e976cSAndreas Gohr     * @return bool false if the logging failed
92642e976cSAndreas Gohr     */
93642e976cSAndreas Gohr    public static function logException($e)
94642e976cSAndreas Gohr    {
95642e976cSAndreas Gohr        global $conf;
96642e976cSAndreas Gohr
97642e976cSAndreas Gohr        $log = join("\t", [gmdate('c'), get_class($e), $e->getFile() . ':' . $e->getLine(), $e->getMessage()]) . "\n";
98642e976cSAndreas Gohr        return io_saveFile($conf['cachedir'] . '/_error.log', $log, true);
99642e976cSAndreas Gohr    }
100*7e0b27f2SAndreas Gohr
101*7e0b27f2SAndreas Gohr    /**
102*7e0b27f2SAndreas Gohr     * Checks the the stacktrace for plugin files
103*7e0b27f2SAndreas Gohr     *
104*7e0b27f2SAndreas Gohr     * @param \Throwable $e
105*7e0b27f2SAndreas Gohr     * @return false|string
106*7e0b27f2SAndreas Gohr     */
107*7e0b27f2SAndreas Gohr    protected static function guessPlugin($e)
108*7e0b27f2SAndreas Gohr    {
109*7e0b27f2SAndreas Gohr        foreach ($e->getTrace() as $line) {
110*7e0b27f2SAndreas Gohr            if (
111*7e0b27f2SAndreas Gohr                isset($line['class']) &&
112*7e0b27f2SAndreas Gohr                preg_match('/\w+?_plugin_(\w+)/', $line['class'], $match)
113*7e0b27f2SAndreas Gohr            ) {
114*7e0b27f2SAndreas Gohr                return $match[1];
115*7e0b27f2SAndreas Gohr            }
116*7e0b27f2SAndreas Gohr
117*7e0b27f2SAndreas Gohr            if (
118*7e0b27f2SAndreas Gohr                isset($line['file']) &&
119*7e0b27f2SAndreas Gohr                preg_match('/lib\/plugins\/(\w+)\//', str_replace('\\', '/', $line['file']), $match)
120*7e0b27f2SAndreas Gohr            ) {
121*7e0b27f2SAndreas Gohr                return $match[1];
122*7e0b27f2SAndreas Gohr            }
123*7e0b27f2SAndreas Gohr        }
124*7e0b27f2SAndreas Gohr
125*7e0b27f2SAndreas Gohr        return false;
126*7e0b27f2SAndreas Gohr    }
127642e976cSAndreas Gohr}
128