13e7f18fbea7bd513878d2d475666cb8e7152fc8
[mdref/mdref] / mdref / ExceptionHandler.php
1 <?php
2
3 namespace mdref;
4
5 use http\Env as HTTP;
6
7 /**
8 * Exception and error handler
9 */
10 class ExceptionHandler
11 {
12 /**
13 * Set up error/exception/shutdown handler
14 */
15 public function __construct() {
16 set_exception_handler($this);
17 set_error_handler($this);
18 register_shutdown_function($this);
19 }
20
21 /**
22 * The exception/error/shutdown handler callback
23 */
24 public function __invoke($e = null, $msg = null) {
25 if ($e instanceof \Exception) {
26 try {
27 echo static::htmlException($e);
28 } catch (\Exception $ignore) {
29 headers_sent() or HTTP::setResponseCode(500);
30 die("FATAL ERROR");
31 }
32 } elseif (isset($e, $msg)) {
33 throw new \Exception($msg, $e);
34 } elseif (($error = error_get_last())) {
35 switch ($error["type"]) {
36 case E_PARSE:
37 case E_ERROR:
38 case E_USER_ERROR:
39 case E_CORE_ERROR:
40 case E_COMPILE_ERROR:
41 while (ob_get_level()) {
42 if (!@ob_end_clean()) {
43 break;
44 }
45 }
46 $message = sprintf("%s in %s at line %d",
47 $error["message"], $error["file"], $error["line"]);
48 echo static::htmlError("Application Error", $message, 500, "");
49 break;
50 }
51 }
52 }
53
54 /**
55 * Format an exception as HTML and send appropriate exception info as HTTP headers
56 * @param \Exception $e
57 * @param array $title_tag
58 * @param array $message_tag
59 * @param array $trace_tag
60 * @return string
61 */
62 public static function htmlException(\Exception $e, array $title_tag = ["h1"], array $message_tag = ["p"], array $trace_tag = ["pre", "style='font-size:smaller;overflow-x:scroll'"]) {
63 if ($e instanceof \http\Controller\Exception) {
64 $code = $e->getCode() ?: 500;
65 foreach ($e->getHeaders() as $key => $val) {
66 HTTP::setResponseHeader($key, $val);
67 }
68 } else {
69 $code = 500;
70 }
71
72 for ($html = ""; $e; $e = $e->getPrevious()) {
73 $html .= static::htmlError(HTTP::getResponseStatusForCode($code),
74 $e->getMessage(), $code, $e->getTraceAsString(),
75 $title_tag, $message_tag, $trace_tag);
76 }
77 return $html;
78 }
79
80 /**
81 * Format an error as HTML
82 * @param string $title
83 * @param string $message
84 * @param int $code
85 * @param string $trace
86 * @param array $title_tag
87 * @param array $message_tag
88 * @param array $trace_tag
89 * @return string
90 */
91 public static function htmlError($title, $message, $code, $trace = null, array $title_tag = ["h1"], array $message_tag = ["p"], array $trace_tag = ["pre", "style='font-size:smaller;overflow-x:scroll'"]) {
92 HTTP::setResponseCode($code);
93
94 $html = sprintf("<%s>%s</%s>\n<%s>%s</%s>\n",
95 implode(" ", $title_tag), $title, $title_tag[0],
96 implode(" ", $message_tag), $message, $message_tag[0]);
97 if ($trace_tag) {
98 if (!isset($trace)) {
99 ob_start();
100 debug_print_backtrace();
101 $trace = ob_get_clean();
102 }
103 if (!empty($trace)) {
104 $html .= sprintf("<%s>%s</%s>\n",
105 implode(" ", $trace_tag), $trace, $trace_tag[0]);
106 }
107 }
108
109 return $html;
110 }
111 }