Browse Source

Cleaning up, Fixing #9

2.1.0
Dustin Wilson 1 year ago
parent
commit
83eafab08a
  1. 56
      lib/Catcher.php
  2. 4
      lib/Catcher/Error.php
  3. 26
      lib/Catcher/Handler.php
  4. 4
      lib/Catcher/JSONHandler.php
  5. 25
      lib/Catcher/PlainTextHandler.php
  6. 24
      lib/Catcher/ThrowableController.php
  7. 4
      run

56
lib/Catcher.php

@ -1,8 +1,8 @@
<?php
/**
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
@ -23,15 +23,15 @@ class Catcher {
/** When set to true Catcher will throw errors as throwables */
public bool $throwErrors = true;
/**
* Stores the error reporting level set by Catcher to compare against when
* unregistering
/**
* Stores the error reporting level set by Catcher to compare against when
* unregistering
*/
protected ?int $errorReporting = null;
/**
/**
* Array of handlers the exceptions are passed to
*
* @var Handler[]
*
* @var Handler[]
*/
protected array $handlers = [];
/** Flag set when the shutdown handler is run */
@ -71,7 +71,7 @@ class Catcher {
if (count($this->handlers) === 1) {
throw new \Exception("Popping the last handler will cause the Catcher to have zero handlers; there must be at least one\n");
}
return array_pop($this->handlers);
}
@ -97,7 +97,7 @@ class Catcher {
return false;
}
// If the current error reporting level has E_ERROR then remove it and store for
// If the current error reporting level has E_ERROR then remove it and store for
// comparison when unregistering
$errorReporting = error_reporting();
if ($errorReporting & \E_ERROR) {
@ -121,7 +121,7 @@ class Catcher {
if (count($this->handlers) === 1) {
throw new \Exception("Shifting the last handler will cause the Catcher to have zero handlers; there must be at least one\n");
}
return array_shift($this->handlers);
}
@ -133,7 +133,7 @@ class Catcher {
restore_error_handler();
restore_exception_handler();
// If error reporting has been set when registering and the error reporting level
// If error reporting has been set when registering and the error reporting level
// is the same as it was when it was set then add E_ERROR back to the error
$errorReporting = error_reporting();
if ($this->errorReporting !== null && $this->errorReporting === $errorReporting) {
@ -172,21 +172,21 @@ class Catcher {
}
/**
* Converts regular errors into throwable Errors for easier handling; meant to be
/**
* Converts regular errors into throwable Errors for easier handling; meant to be
* used with set_error_handler.
*
*
* @internal
*/
public function handleError(int $code, string $message, ?string $file = null, ?int $line = null): bool {
if ($code && $code & error_reporting()) {
$error = new Error($message, $code, $file, $line);
if ($this->throwErrors) {
// The point of this library is to allow treating of errors as if they were
// exceptions but instead have things like warnings, notices, etc. not stop
// execution. You normally can't have it both ways. So, what's going on here is
// that if the error wouldn't normally stop execution the newly-created Error
// throwable is thrown in a fork instead, allowing execution to resume in the
// The point of this library is to allow treating of errors as if they were
// exceptions but instead have things like warnings, notices, etc. not stop
// execution. You normally can't have it both ways. So, what's going on here is
// that if the error wouldn't normally stop execution the newly-created Error
// throwable is thrown in a fork instead, allowing execution to resume in the
// parent process.
if ($this->isErrorFatal($code)) {
throw $error;
@ -199,7 +199,7 @@ class Catcher {
// This can't be covered because it happens in the fork
throw $error; // @codeCoverageIgnore
}
pcntl_wait($status);
return true;
}
@ -208,14 +208,14 @@ class Catcher {
$this->handleThrowable($error);
return true;
}
// If preventing exit we don't want a false here to halt processing
return ($this->preventExit);
}
/**
/**
* Handles both Exceptions and Errors; meant to be used with set_exception_handler.
*
*
* @internal
*/
public function handleThrowable(\Throwable $throwable): void {
@ -235,7 +235,7 @@ class Catcher {
if (
$this->isShuttingDown ||
$controlCode & Handler::EXIT ||
$throwable instanceof \Exception ||
$throwable instanceof \Exception ||
($throwable instanceof Error && $this->isErrorFatal($throwable->getCode())) ||
(!$throwable instanceof Error && $throwable instanceof \Error)
) {
@ -248,7 +248,7 @@ class Catcher {
$this->lastThrowable = $throwable;
// Don't want to exit here when shutting down so any shutdown functions further
// Don't want to exit here when shutting down so any shutdown functions further
// down the stack still run.
if (!$this->preventExit && !$this->isShuttingDown) {
$this->exit(max($throwable->getCode(), 1));
@ -258,9 +258,9 @@ class Catcher {
$this->lastThrowable = $throwable;
}
/**
/**
* Handles shutdowns, passes all possible built-in error codes to the error handler.
*
*
* @internal
*/
public function handleShutdown(): void {

4
lib/Catcher/Error.php

@ -1,8 +1,8 @@
<?php
/**
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);

26
lib/Catcher/Handler.php

@ -1,8 +1,8 @@
<?php
/**
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
@ -23,27 +23,27 @@ abstract class Handler {
public const NOW = 32;
/**
/**
* Array of HandlerOutputs the handler creates
*
* @var HandlerOutput[]
*
* @var array[]
*/
protected array $outputBuffer = [];
/** The number of backtrace frames in which to print arguments; defaults to 5 */
protected int $_backtraceArgFrameLimit = 5;
/**
* The character encoding used for errors; only used if headers weren't sent before
* an error occurred
/**
* The character encoding used for errors; only used if headers weren't sent before
* an error occurred
*/
protected string $_charset = 'UTF-8';
/** If true the handler will force break the loop through the stack of handlers */
protected bool $_forceBreak = false;
/** If true the handler will force an exit */
protected bool $_forceExit = false;
/**
* If true the handler will output as soon as possible; however, if silent
* is true the handler will output nothing
/**
* If true the handler will output as soon as possible; however, if silent
* is true the handler will output nothing
*/
protected bool $_forceOutputNow = false;
/** The HTTP code to be sent; possible values: 200, 400-599 */
@ -60,9 +60,9 @@ abstract class Handler {
protected bool $_silent = false;
/** The PHP-standard date format which to use for timestamps in output */
protected string $_timeFormat = 'Y-m-d\TH:i:s.vO';
/**
/**
* A user-defined closure to use when printing arguments in backtraces
*
*
* @var ?(mixed): string|bool
*/
protected ?\Closure $_varExporter = null;

4
lib/Catcher/JSONHandler.php

@ -1,8 +1,8 @@
<?php
/**
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);

25
lib/Catcher/PlainTextHandler.php

@ -1,8 +1,8 @@
<?php
/**
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
@ -35,7 +35,7 @@ class PlainTextHandler extends Handler {
if ($o['outputCode'] & self::SILENT) {
continue;
}
$this->print($this->serializeOutputThrowable($o));
}
}
@ -47,34 +47,35 @@ class PlainTextHandler extends Handler {
}
protected function log(\Throwable $throwable, string $message): void {
$context = [ 'exception' => $throwable ];
if ($throwable instanceof \Error) {
switch ($throwable->getCode()) {
case \E_NOTICE:
case \E_USER_NOTICE:
case \E_STRICT:
$this->_logger->notice($message);
$this->_logger->notice($message, $context);
break;
case \E_WARNING:
case \E_COMPILE_WARNING:
case \E_USER_WARNING:
case \E_DEPRECATED:
case \E_USER_DEPRECATED:
$this->_logger->warning($message);
$this->_logger->warning($message, $context);
break;
case \E_RECOVERABLE_ERROR:
$this->_logger->error($message);
$this->_logger->error($message, $context);
break;
case \E_PARSE:
case \E_CORE_ERROR:
case \E_COMPILE_ERROR:
$this->_logger->alert($message);
$this->_logger->alert($message, $context);
break;
default: $this->_logger->critical($message);
default: $this->_logger->critical($message, $context);
}
} elseif ($throwable instanceof \Exception && ($throwable instanceof \PharException || $throwable instanceof \RuntimeException)) {
$this->_logger->alert($message);
$this->_logger->alert($message, $context);
} else {
$this->_logger->critical($message);
$this->_logger->critical($message, $context);
}
}
@ -83,7 +84,7 @@ class PlainTextHandler extends Handler {
if ($class !== null && !empty($outputThrowable['errorType'])) {
$class = "{$outputThrowable['errorType']} ($class)";
}
$output = sprintf(
'%s: %s in file %s on line %d' . \PHP_EOL,
$class,
@ -118,7 +119,7 @@ class PlainTextHandler extends Handler {
} elseif (!empty($frame['function'])) {
$method = $frame['function'];
}
$output .= sprintf("%{$maxDigits}d. %s %s:%d" . \PHP_EOL,
$key + 1,
$method,

24
lib/Catcher/ThrowableController.php

@ -1,8 +1,8 @@
<?php
/**
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
@ -37,7 +37,7 @@ class ThrowableController {
}
switch ($this->throwable->getCode()) {
case \E_ERROR:
case \E_ERROR:
$this->errorType = 'PHP Fatal Error';
break;
case \E_WARNING:
@ -49,7 +49,7 @@ class ThrowableController {
case \E_NOTICE:
$this->errorType = 'PHP Notice';
break;
case \E_CORE_ERROR:
case \E_CORE_ERROR:
$this->errorType = 'PHP Core Error';
break;
case \E_CORE_WARNING:
@ -99,17 +99,17 @@ class ThrowableController {
if (
!$this->throwable instanceof \Error ||
!in_array($this->throwable->getCode(), [ E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING ]) ||
!extension_loaded('xdebug') ||
!function_exists('xdebug_info') ||
sizeof(\xdebug_info('mode')) === 0
!extension_loaded('xdebug') ||
!function_exists('xdebug_info') ||
sizeof(xdebug_info('mode')) === 0
) {
$frames = $this->throwable->getTrace();
} else {
$frames = array_values(array_diff_key(xdebug_get_function_stack(), debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS)));
}
// PHP for some stupid reason thinks it's okay not to provide line numbers and file
// names when using call_user_func_array; this fixes that.
// PHP for some stupid reason thinks it's okay not to provide line numbers and file
// names when using call_user_func_array; this fixes that.
// (https://bugs.php.net/bug.php?id=44428)
foreach ($frames as $key => $frame) {
if (empty($frame['file'])) {
@ -119,9 +119,9 @@ class ThrowableController {
$next = $frames[$key + 1] ?? [];
if (
!empty($next['file']) &&
!empty($next['file']) &&
!empty($next['function']) &&
!empty($next['line']) &&
!empty($next['line']) &&
str_contains($next['function'], 'call_user_func')
) {
$file = $next['file'];
@ -164,7 +164,7 @@ class ThrowableController {
if ($this->throwable instanceof \Error) {
$errorType = $this->getErrorType();
if ($errorType !== null) {
$f['code'] = $this->throwable->getCode();
$f['code'] = $this->throwable->getCode();
$f['errorType'] = $errorType;
}
}

4
run

@ -10,7 +10,7 @@ function help($error = true): void {
Usage:
run test [additional_phpunit_options]
run --help
USAGE;
if ($error) {
@ -48,7 +48,7 @@ switch ($argv[1]) {
case '--help':
help(false);
break;
default:
default:
help();
}

Loading…
Cancel
Save