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