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