diff --git a/composer.json b/composer.json index 477bd24..4ab3bfc 100644 --- a/composer.json +++ b/composer.json @@ -18,5 +18,8 @@ "require": { "php": ">=8.1", "psr/log": "^3.0" + }, + "suggest": { + "ext-dom": "For HTMLHandler" } } diff --git a/lib/Catcher.php b/lib/Catcher.php index 1616416..454d43d 100644 --- a/lib/Catcher.php +++ b/lib/Catcher.php @@ -80,7 +80,12 @@ class Catcher { } } - if ($this->isShuttingDown || $throwable instanceof \Exception || in_array($throwable->getCode(), [ \E_ERROR, \E_PARSE, \E_CORE_ERROR, \E_COMPILE_ERROR, \E_USER_ERROR ])) { + if ( + self::isHTTPRequest() || + $this->isShuttingDown || + $throwable instanceof \Exception || + in_array($throwable->getCode(), [ \E_ERROR, \E_PARSE, \E_CORE_ERROR, \E_COMPILE_ERROR, \E_USER_ERROR ]) + ) { exit($throwable->getCode()); } } @@ -98,4 +103,8 @@ class Catcher { $this->handleError($error['type'], $error['message'], $error['file'], $error['line']); } } + + public static function isHTTPRequest() { + return (\PHP_SAPI !== 'cli' && isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD'])); + } } \ No newline at end of file diff --git a/lib/Catcher/HTMLHandler.php b/lib/Catcher/HTMLHandler.php new file mode 100644 index 0000000..8c1042c --- /dev/null +++ b/lib/Catcher/HTMLHandler.php @@ -0,0 +1,216 @@ +_getBacktraceArgFrameLimit; + } + + public function getOutputBacktrace(): bool { + return $this->_outputBacktrace; + } + + public function getOutputPrevious(): bool { + return $this->_outputPrevious; + } + + public function getOutputTime(): bool { + return $this->_outputTime; + } + + public function getTimeFormat(): bool { + return $this->_timeFormat; + } + + public function handle(\Throwable $throwable, ThrowableController $controller): bool { + $document = new \DOMDocument(); + $frag = $document->createDocumentFragment(); + + if ($this->_outputTime && $this->_timeFormat !== '') { + $p = $document->createElement('p'); + $time = $document->createElement('time'); + $time->appendChild($document->createTextNode((new \DateTime())->format($this->_timeFormat))); + $p->appendChild($time); + $frag->appendChild($p); + } + + $frag->appendChild($this->buildThrowable($document, $throwable, $controller)); + if ($this->_outputPrevious) { + $prev = $throwable->getPrevious(); + $prevController = $controller->getPrevious(); + while ($prev) { + $p = $document->createElement('p'); + $small = $document->createElement('small'); + $small->appendChild($document->createTextNode('Caused by ↴')); + $p->appendChild($small); + $frag->appendChild($p); + $frag->appendChild($this->buildThrowable($document, $prev, $prevController)); + $prev = $prev->getPrevious(); + $prevController = $prevController->getPrevious(); + } + } + + if ($this->_outputBacktrace) { + $frames = $controller->getFrames(); + $p = $document->createElement('p'); + $p->appendChild($document->createTextNode('Stack trace:')); + $frag->appendChild($p); + + if (count($frames) > 0) { + $ol = $document->createElement('ol'); + $p->appendChild($ol); + $num = 1; + foreach ($frames as $frame) { + $li = $document->createElement('li'); + $args = (!empty($frame['args']) && $this->_backtraceArgFrameLimit >= $num); + $t = ($args) ? $document->createElement('p') : $li; + + if (!empty($frame['error'])) { + $b = $document->createElement('b'); + $b->appendChild($document->createTextNode($frame['error'])); + $t->appendChild($b); + $t->appendChild($document->createTextNode(' (')); + $code = $document->createElement('code'); + $code->appendChild($document->createTextNode($frame['class'])); + $t->appendChild($code); + $t->appendChild($document->createTextNode(')')); + } elseif (!empty($frame['class'])) { + $code = $document->createElement('code'); + $code->appendChild($document->createTextNode($frame['class'])); + $t->appendChild($code); + } + + $class = $frame['class'] ?? ''; + $function = $frame['function'] ?? ''; + if ($function) { + if ($class) { + $code->firstChild->textContent .= "::{$function}()"; + } else { + $code = $document->createElement('code'); + $code->appendChild($document->createTextNode("{$function}()")); + $t->appendChild($code); + } + } + + $t->appendChild($document->createTextNode(' ')); + $i = $document->createElement('i'); + $i->appendChild($document->createTextNode($frame['file'])); + $t->appendChild($i); + $t->appendChild($document->createTextNode(":{$frame['line']}")); + + if ($args) { + $li->appendChild($t); + $pre = $document->createElement('pre'); + $pre->appendChild($document->createTextNode(var_export($frame['args'], true))); + $li->appendChild($pre); + } + + $ol->appendChild($li); + } + } + } + + $this->_result = $frag; + + if (\PHP_SAPI !== 'cli' && $this->_output) { + $document->loadHTML(sprintf( + '