introduce a global error handler

This transfers old style PHP errors into Exceptions and installs a
global exception handler. All exceptions caught by this handler are
logged to an error log and a meassage is shown to the end user. This
should finally get rid of "blank page" bug reports.
This commit is contained in:
Andreas Gohr 2020-07-13 19:05:12 +02:00
parent 04225b4a35
commit 642e976cb6
3 changed files with 139 additions and 30 deletions

98
inc/ErrorHandler.php Normal file
View File

@ -0,0 +1,98 @@
<?php
namespace dokuwiki;
class ErrorHandler
{
/**
* Register the default error handling
*/
public static function register()
{
set_error_handler([ErrorHandler::class, 'errorConverter']);
if (!defined('DOKU_UNITTEST')) {
set_exception_handler([ErrorHandler::class, 'fatalException']);
}
}
/**
* Default Exception handler to show a nice user message before dieing
*
* The exception is logged to the error log
*
* @param \Throwable $e
*/
public static function fatalException($e)
{
$title = hsc(get_class($e) . ': ' . $e->getMessage());
$msg = 'An unforeseen error has occured. This is most likely a bug somewhere.';
$logged = self::logException($e)
? 'More info has been written to the DokuWiki _error.log'
: $e->getFile() . ':' . $e->getLine();
echo <<<EOT
<!DOCTYPE html>
<html>
<head><title>$title</title></head>
<body style="font-family: Arial, sans-serif">
<div style="width:60%; margin: auto; background-color: #fcc;
border: 1px solid #faa; padding: 0.5em 1em;">
<h1 style="font-size: 120%">$title</h1>
<p>$msg</p>
<p>$logged</p>
</div>
</body>
</html>
EOT;
}
/**
* Convenience method to display an error message for the given Exception
*
* @param \Throwable $e
* @param string $intro
*/
public static function showExceptionMsg($e, $intro = 'Error!')
{
$msg = $intro . get_class($e) . ': ' . $e->getMessage();
self::logException($e);
msg(hsc($msg), -1);
}
/**
* Default error handler to convert old school warnings, notices, etc to exceptions
*
* You should not need to call this directly!
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @return bool
* @throws \ErrorException
*/
public static function errorConverter($errno, $errstr, $errfile, $errline)
{
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting, so let it fall
// through to the standard PHP error handler
return false;
}
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
}
/**
* Log the given exception to the error log
*
* @param \Throwable $e
* @return bool false if the logging failed
*/
public static function logException($e)
{
global $conf;
$log = join("\t", [gmdate('c'), get_class($e), $e->getFile() . ':' . $e->getLine(), $e->getMessage()]) . "\n";
return io_saveFile($conf['cachedir'] . '/_error.log', $log, true);
}
}

View File

@ -2,6 +2,8 @@
namespace dokuwiki\Extension;
use dokuwiki\ErrorHandler;
/**
* Class to encapsulate access to dokuwiki plugins
*
@ -90,42 +92,48 @@ class PluginController
$class = $type . '_plugin_' . $name;
//plugin already loaded?
if (!empty($DOKU_PLUGINS[$type][$name])) {
if ($new || !$DOKU_PLUGINS[$type][$name]->isSingleton()) {
return class_exists($class, true) ? new $class : null;
try {
//plugin already loaded?
if (!empty($DOKU_PLUGINS[$type][$name])) {
if ($new || !$DOKU_PLUGINS[$type][$name]->isSingleton()) {
return class_exists($class, true) ? new $class : null;
}
return $DOKU_PLUGINS[$type][$name];
}
return $DOKU_PLUGINS[$type][$name];
}
//construct class and instantiate
if (!class_exists($class, true)) {
# the plugin might be in the wrong directory
$inf = confToHash(DOKU_PLUGIN . "$plugin/plugin.info.txt");
if ($inf['base'] && $inf['base'] != $plugin) {
msg(
sprintf(
"Plugin installed incorrectly. Rename plugin directory '%s' to '%s'.",
hsc($plugin),
hsc(
$inf['base']
)
), -1
);
} elseif (preg_match('/^' . DOKU_PLUGIN_NAME_REGEX . '$/', $plugin) !== 1) {
msg(
sprintf(
"Plugin name '%s' is not a valid plugin name, only the characters a-z and 0-9 are allowed. " .
'Maybe the plugin has been installed in the wrong directory?', hsc($plugin)
), -1
);
//construct class and instantiate
if (!class_exists($class, true)) {
# the plugin might be in the wrong directory
$inf = confToHash(DOKU_PLUGIN . "$plugin/plugin.info.txt");
if ($inf['base'] && $inf['base'] != $plugin) {
msg(
sprintf(
"Plugin installed incorrectly. Rename plugin directory '%s' to '%s'.",
hsc($plugin),
hsc(
$inf['base']
)
), -1
);
} elseif (preg_match('/^' . DOKU_PLUGIN_NAME_REGEX . '$/', $plugin) !== 1) {
msg(
sprintf(
"Plugin name '%s' is not a valid plugin name, only the characters a-z and 0-9 are allowed. " .
'Maybe the plugin has been installed in the wrong directory?', hsc($plugin)
), -1
);
}
return null;
}
$DOKU_PLUGINS[$type][$name] = new $class;
} catch (\Exception $e) {
ErrorHandler::showExceptionMsg($e, sprintf('Failed to load plugin %s', $plugin));
return null;
}
$DOKU_PLUGINS[$type][$name] = new $class;
return $DOKU_PLUGINS[$type][$name];
}

View File

@ -199,6 +199,9 @@ if (empty($plugin_controller_class)) $plugin_controller_class = dokuwiki\Extensi
require_once(DOKU_INC.'vendor/autoload.php');
require_once(DOKU_INC.'inc/load.php');
// from now on everything is an exception
\dokuwiki\ErrorHandler::register();
// disable gzip if not available
define('DOKU_HAS_BZIP', function_exists('bzopen'));
define('DOKU_HAS_GZIP', function_exists('gzopen'));