Browse Source

Added secondary error handling in Handler

This should allow Catcher to print errors even if there are errors encountered
when logging or exporting variables in stack traces
2.1.0
Dustin Wilson 10 months ago
parent
commit
a525fad96a
  1. 63
      lib/Catcher/Handler.php
  2. 2
      lib/Catcher/PlainTextHandler.php

63
lib/Catcher/Handler.php

@ -7,7 +7,8 @@
declare(strict_types=1);
namespace MensBeam\Catcher;
use Psr\Log\LoggerInterface;
use MensBeam\Catcher,
Psr\Log\LoggerInterface;
abstract class Handler {
@ -146,6 +147,49 @@ abstract class Handler {
return $output;
}
/**
* If an error is triggered while logging the error would be output by PHP's
* handler. This is used to attempt as best as possible to have the error be
* output by Catcher.
*
* @internal
*/
public function handleError(int $code, string $message, ?string $file = null, ?int $line = null): bool {
if ($code && $code & error_reporting()) {
// PHP's method for getting the current exception handler is stupid,
// but that's how it is...
$exceptionHandler = set_exception_handler(null);
set_exception_handler($exceptionHandler);
// If the current exception handler happens to not be Catcher use PHP's handler
// instead; this shouldn't happen but is here just in case
if (!is_array($exceptionHandler) || !$exceptionHandler[0] instanceof Catcher) {
return false;
}
$catcher = $exceptionHandler[0];
$handlers = $catcher->getHandlers();
$silent = false;
foreach ($handlers as $h) {
$h->setOption('logger', null);
$h->setOption('varExporter', null);
$silent = (!$silent) ? $h->getOption('silent') : $silent;
}
// If all of the handlers are silent then use PHP's handler instead; this is
// because a valid use for Catcher is to have it be silent but instead have the
// logger print the errors to stderr/stdout; if there is an error in the logger
// then it wouldn't print.
if ($silent) {
return false;
}
$catcher->handleThrowable(new Error($message, $code, $file, $line));
}
return true;
}
public function setOption(string $name, mixed $value): void {
$class = get_class($this);
if (!property_exists($class, "_$name")) {
@ -219,7 +263,12 @@ abstract class Handler {
abstract protected function invokeCallback(): void;
protected function log(\Throwable $throwable, string $message): void {
if ($this->_logger === null) {
return;
}
$context = [ 'exception' => $throwable ];
set_error_handler([ $this, 'handleError' ]);
if ($throwable instanceof \Error) {
switch ($throwable->getCode()) {
case \E_NOTICE:
@ -246,6 +295,7 @@ abstract class Handler {
} else {
$this->_logger->critical($message, $context);
}
restore_error_handler();
}
protected function print(string $string): void {
@ -257,4 +307,15 @@ abstract class Handler {
echo $string;
}
protected function varExporter(mixed $value): string|bool {
$exporter = $this->_varExporter;
set_error_handler([ $this, 'handleError' ]);
$value = $exporter($value);
restore_error_handler();
return $value;
}
}

2
lib/Catcher/PlainTextHandler.php

@ -86,7 +86,7 @@ class PlainTextHandler extends Handler {
if (!empty($frame['args']) && $this->_backtraceArgFrameLimit > $key) {
$varExporter = $this->_varExporter;
$output .= preg_replace('/^/m', "$indent| ", $varExporter($frame['args'])) . \PHP_EOL;
$output .= preg_replace('/^/m', "$indent| ", $this->varExporter($frame['args'])) . \PHP_EOL;
}
}

Loading…
Cancel
Save