diff options
author | Arnold Daniels <arnold@jasny.net> | 2017-01-03 22:27:47 +0100 |
---|---|---|
committer | Arnold Daniels <arnold@jasny.net> | 2017-01-03 22:27:47 +0100 |
commit | ac34b91af334058bede75652f1872e75a0fff623 (patch) | |
tree | 399dab583411452adda670e095ec4f4ad2d3944c | |
parent | 63ec91cda3794f07da8eb8b0ef4aaff08e342a08 (diff) | |
download | error-handler-ac34b91af334058bede75652f1872e75a0fff623.zip error-handler-ac34b91af334058bede75652f1872e75a0fff623.tar.gz error-handler-ac34b91af334058bede75652f1872e75a0fff623.tar.bz2 |
Split up ErrorHandler class into traits
-rw-r--r-- | src/ErrorHandler.php | 337 | ||||
-rw-r--r-- | src/ErrorHandler/FunctionWrapper.php | 65 | ||||
-rw-r--r-- | src/ErrorHandler/HandleShutdownError.php | 96 | ||||
-rw-r--r-- | src/ErrorHandler/HandleUncaughtError.php | 154 | ||||
-rw-r--r-- | src/ErrorHandler/HandleUncaughtException.php | 128 | ||||
-rw-r--r-- | tests/ErrorHandlerTest.php | 6 |
6 files changed, 467 insertions, 319 deletions
diff --git a/src/ErrorHandler.php b/src/ErrorHandler.php index 9eabfd7..7250b7f 100644 --- a/src/ErrorHandler.php +++ b/src/ErrorHandler.php @@ -2,8 +2,7 @@ namespace Jasny; -use Jasny\ErrorHandler\ErrorCodes; -use Jasny\ErrorHandler\Logging; +use Jasny\ErrorHandler; use Jasny\ErrorHandler\Middleware; use Psr\Log\LoggerAwareInterface; @@ -12,52 +11,18 @@ use Psr\Log\LoggerAwareInterface; */ class ErrorHandler implements LoggerAwareInterface { - use Logging; - use ErrorCodes; - - /** - * @var \Exception|\Error - */ - protected $error; - - /** - * @var callable|false - */ - protected $chainedErrorHandler; + use ErrorHandler\Logging; + use ErrorHandler\ErrorCodes; + use ErrorHandler\HandleUncaughtError; + use ErrorHandler\HandleShutdownError; + use ErrorHandler\HandleUncaughtException; + use ErrorHandler\FunctionWrapper; - /** - * @var callable|false - */ - protected $chainedExceptionHandler; /** - * @var boolean - */ - protected $registeredShutdown = false; - - /** - * Convert fatal errors to exceptions - * @var boolean - */ - protected $convertFatalErrors = false; - - /** - * Log the following error types (in addition to caugth errors) - * @var int - */ - protected $logErrorTypes = 0; - - /** - * Log the following exception classes (and subclasses) - * @var array - */ - protected $logExceptionClasses = []; - - /** - * A string which reserves memory that can be used to log the error in case of an out of memory fatal error - * @var string + * @var \Exception|\Error */ - protected $reservedMemory; + protected $error; /** * @var callback @@ -92,55 +57,16 @@ class ErrorHandler implements LoggerAwareInterface } - /** - * Get the error handler that has been replaced. - * - * @return callable|false|null - */ - public function getChainedErrorHandler() - { - return $this->chainedErrorHandler; - } - - /** - * Get the error handler that has been replaced. - * - * @return callable|false|null - */ - public function getChainedExceptionHandler() - { - return $this->chainedExceptionHandler; - } - - /** - * Get the types of errors that will be logged - * - * @return int Binary set of E_* constants - */ - public function getLoggedErrorTypes() - { - return $this->logErrorTypes; - } - - /** - * Get a list of Exception and other Throwable classes that will be logged - * @return array - */ - public function getLoggedExceptionClasses() - { - return $this->logExceptionClasses; - } - /** - * Use the global error handler to convert E_USER_ERROR and E_RECOVERABLE_ERROR to an ErrorException + * Use this error handler as middleware */ - public function converErrorsToExceptions() + public function asMiddleware() { - $this->convertFatalErrors = true; - $this->initErrorHandler(); + return new Middleware($this); } - + + /** * Log these types of errors or exceptions * @@ -156,40 +82,6 @@ class ErrorHandler implements LoggerAwareInterface throw new \InvalidArgumentException("Type should be an error code (int) or Exception class (string)"); } } - - /** - * Log these types of errors or exceptions - * - * @param string $class Exception class name - */ - protected function logUncaughtException($class) - { - if (!in_array($class, $this->logExceptionClasses)) { - $this->logExceptionClasses[] = $class; - } - - $this->initExceptionHandler(); - } - - /** - * Log these types of errors or exceptions - * - * @param int $type E_* contants as binary set - */ - protected function logUncaughtErrors($type) - { - $this->logErrorTypes |= $type; - - $unhandled = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR; - - if ($type & ~$unhandled) { - $this->initErrorHandler(); - } - - if ($type & $unhandled) { - $this->initShutdownFunction(); - } - } /** @@ -210,153 +102,19 @@ class ErrorHandler implements LoggerAwareInterface }; } } - - /** - * Use this error handler as middleware - */ - public function asMiddleware() - { - return new Middleware($this); - } - - - /** - * Use the global error handler - */ - protected function initErrorHandler() - { - if (!isset($this->chainedErrorHandler)) { - $this->chainedErrorHandler = $this->setErrorHandler([$this, 'handleError']) ?: false; - } - } - - /** - * Uncaught error handler - * @ignore - * - * @param int $type - * @param string $message - * @param string $file - * @param int $line - * @param array $context - */ - public function handleError($type, $message, $file, $line, $context) - { - if ($this->errorReporting() & $type) { - $error = new \ErrorException($message, 0, $type, $file, $line); - - if ($this->convertFatalErrors && ($type & (E_RECOVERABLE_ERROR | E_USER_ERROR))) { - throw $error; - } - - if ($this->logErrorTypes & $type) { - $this->log($error); - } - } - - return $this->chainedErrorHandler - ? call_user_func($this->chainedErrorHandler, $type, $message, $file, $line, $context) - : false; - } - /** - * Use the global error handler - */ - protected function initExceptionHandler() - { - if (!isset($this->chainedExceptionHandler)) { - $this->chainedExceptionHandler = $this->setExceptionHandler([$this, 'handleException']) ?: false; - } - } - - /** - * Uncaught exception handler - * @ignore + * Run the fatal error callback * - * @param \Exception|\Error $exception - */ - public function handleException($exception) - { - $this->setExceptionHandler(null); - $this->setErrorHandler(null); - - $isInstanceOf = array_map(function($class) use ($exception) { - return is_a($exception, $class); - }, $this->logExceptionClasses); - - if ($exception instanceof \Error || $exception instanceof \ErrorException) { - $type = $exception instanceof \Error ? $exception->getCode() : $exception->getSeverity(); - $shouldLog = $this->logErrorTypes & $type; - } else { - $shouldLog = array_sum($isInstanceOf) > 0; - } - - if ($shouldLog) { - $this->log($exception); - } - - if ($this->onFatalError) { - call_user_func($this->onFatalError, $exception); - } - - if ($this->chainedExceptionHandler) { - call_user_func($this->chainedExceptionHandler, $exception); - } - - - throw $exception; // This is now handled by the default exception and error handler - } - - - /** - * Reserve memory for shutdown function in case of out of memory + * @param \Exception|\Error $error */ - protected function reserveMemory() + protected function callOnFatalError($error) { - $this->reservedMemory = str_repeat(' ', 10 * 1024); - } - - /** - * Register a shutdown function - */ - protected function initShutdownFunction() - { - if (!$this->registeredShutdown) { - $this->registerShutdownFunction([$this, 'shutdownFunction']) ?: false; - $this->registeredShutdown = true; - - $this->reserveMemory(); - } - } - - /** - * Called when the script has ends - * @ignore - */ - public function shutdownFunction() - { - $this->reservedMemory = null; - - $err = $this->errorGetLast(); - $unhandled = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR; - - if (!$err || !($err['type'] & $unhandled)) { - return; - } - - $error = new \ErrorException($err['message'], 0, $err['type'], $err['file'], $err['line']); - - if ($err['type'] & $this->logErrorTypes) { - $this->log($error); - } - if ($this->onFatalError) { call_user_func($this->onFatalError, $error); } } - - + /** * Clear and destroy all the output buffers * @codeCoverageIgnore @@ -367,62 +125,5 @@ class ErrorHandler implements LoggerAwareInterface ob_end_clean(); } } - - /** - * Wrapper method for `error_reporting` - * @codeCoverageIgnore - * - * @return int - */ - protected function errorReporting() - { - return error_reporting(); - } - - /** - * Wrapper method for `error_get_last` - * @codeCoverageIgnore - * - * @return array - */ - protected function errorGetLast() - { - return error_get_last(); - } - - /** - * Wrapper method for `set_error_handler` - * @codeCoverageIgnore - * - * @param callable $callback - * @param int $error_types - * @return callable|null - */ - protected function setErrorHandler($callback, $error_types = E_ALL) - { - return set_error_handler($callback, $error_types); - } - - /** - * Wrapper method for `set_exception_handler` - * @codeCoverageIgnore - * - * @param callable $callback - * @return callable|null - */ - protected function setExceptionHandler($callback) - { - return set_exception_handler($callback); - } - - /** - * Wrapper method for `register_shutdown_function` - * @codeCoverageIgnore - * - * @param callable $callback - */ - protected function registerShutdownFunction($callback) - { - register_shutdown_function($callback); - } } + diff --git a/src/ErrorHandler/FunctionWrapper.php b/src/ErrorHandler/FunctionWrapper.php new file mode 100644 index 0000000..073c493 --- /dev/null +++ b/src/ErrorHandler/FunctionWrapper.php @@ -0,0 +1,65 @@ +<?php + +namespace Jasny\ErrorHandler; + +/** + * Wrapper methods for internal PHP functions + * + * @codeCoverageIgnore + */ +trait FunctionWrapper +{ + /** + * Wrapper method for `error_reporting` + * + * @return int + */ + protected function errorReporting() + { + return error_reporting(); + } + + /** + * Wrapper method for `error_get_last` + * + * @return array + */ + protected function errorGetLast() + { + return error_get_last(); + } + + /** + * Wrapper method for `set_error_handler` + * + * @param callable $callback + * @param int $error_types + * @return callable|null + */ + protected function setErrorHandler($callback, $error_types = E_ALL) + { + return set_error_handler($callback, $error_types); + } + + /** + * Wrapper method for `set_exception_handler` + * + * @param callable $callback + * @return callable|null + */ + protected function setExceptionHandler($callback) + { + return set_exception_handler($callback); + } + + /** + * Wrapper method for `register_shutdown_function` + * + * @param callable $callback + */ + protected function registerShutdownFunction($callback) + { + register_shutdown_function($callback); + } +} + diff --git a/src/ErrorHandler/HandleShutdownError.php b/src/ErrorHandler/HandleShutdownError.php new file mode 100644 index 0000000..1e9907b --- /dev/null +++ b/src/ErrorHandler/HandleShutdownError.php @@ -0,0 +1,96 @@ +<?php + +namespace Jasny\ErrorHandler; + +/** + * Trait for handling errors on shutdown + */ +trait HandleShutdownError +{ + /** + * @var boolean + */ + protected $registeredShutdown = false; + + /** + * A string which reserves memory that can be used to log the error in case of an out of memory fatal error + * @var string + */ + protected $reservedMemory; + + + /** + * Run the fatal error callback + * + * @param \Exception|\Error $error + */ + abstract protected function callOnFatalError($error); + + /** + * Wrapper method for `error_get_last` + * + * @return array + */ + abstract protected function errorGetLast(); + + /** + * Wrapper method for `register_shutdown_function` + * + * @param callable $callback + */ + abstract protected function registerShutdownFunction($callback); + + /** + * Log an error or exception + * + * @param \Exception|\Error $error + */ + abstract public function log($error); + + + /** + * Reserve memory for shutdown function in case of out of memory + */ + protected function reserveMemory() + { + $this->reservedMemory = str_repeat(' ', 10 * 1024); + } + + /** + * Register the shutdown function + */ + protected function initShutdownFunction() + { + if (!$this->registeredShutdown) { + $this->registerShutdownFunction([$this, 'shutdownFunction']) ?: false; + $this->registeredShutdown = true; + + $this->reserveMemory(); + } + } + + /** + * Called when the script has ends + * @ignore + */ + public function shutdownFunction() + { + $this->reservedMemory = null; + + $err = $this->errorGetLast(); + $unhandled = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR; + + if (!$err || !($err['type'] & $unhandled)) { + return; + } + + $error = new \ErrorException($err['message'], 0, $err['type'], $err['file'], $err['line']); + + if ($err['type'] & $this->logErrorTypes) { + $this->log($error); + } + + $this->callOnFatalError($error); + } +} + diff --git a/src/ErrorHandler/HandleUncaughtError.php b/src/ErrorHandler/HandleUncaughtError.php new file mode 100644 index 0000000..91b28f3 --- /dev/null +++ b/src/ErrorHandler/HandleUncaughtError.php @@ -0,0 +1,154 @@ +<?php + +namespace Jasny\ErrorHandler; + +/** + * Trait for handling uncaught errors using PHP's error handler + */ +trait HandleUncaughtError +{ + /** + * @var callable|false + */ + protected $chainedErrorHandler; + + /** + * Convert fatal errors to exceptions + * @var boolean + */ + protected $convertFatalErrors = false; + + /** + * Log the following error types (in addition to caugth errors) + * @var int + */ + protected $logErrorTypes = 0; + + + /** + * Run the fatal error callback + * + * @param \Exception|\Error $error + */ + abstract protected function callOnFatalError($error); + + /** + * Wrapper method for `error_reporting` + * + * @return int + */ + abstract protected function errorReporting(); + + /** + * Wrapper method for `set_error_handler` + * + * @param callable $callback + * @param int $error_types + * @return callable|null + */ + abstract protected function setErrorHandler($callback, $error_types = E_ALL); + + /** + * Register the shutdown function + */ + abstract protected function initShutdownFunction(); + + /** + * Log an error or exception + * + * @param \Exception|\Error $error + */ + abstract public function log($error); + + + /** + * Get the error handler that has been replaced. + * + * @return callable|false|null + */ + public function getChainedErrorHandler() + { + return $this->chainedErrorHandler; + } + + /** + * Get the types of errors that will be logged + * + * @return int Binary set of E_* constants + */ + public function getLoggedErrorTypes() + { + return $this->logErrorTypes; + } + + /** + * Log these types of errors or exceptions + * + * @param int $type E_* contants as binary set + */ + protected function logUncaughtErrors($type) + { + $this->logErrorTypes |= $type; + + $unhandled = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR; + + if ($type & ~$unhandled) { + $this->initErrorHandler(); + } + + if ($type & $unhandled) { + $this->initShutdownFunction(); + } + } + + + /** + * Use the global error handler to convert E_USER_ERROR and E_RECOVERABLE_ERROR to an ErrorException + */ + public function converErrorsToExceptions() + { + $this->convertFatalErrors = true; + $this->initErrorHandler(); + } + + + /** + * Use the global error handler + */ + protected function initErrorHandler() + { + if (!isset($this->chainedErrorHandler)) { + $this->chainedErrorHandler = $this->setErrorHandler([$this, 'handleError']) ?: false; + } + } + + /** + * Uncaught error handler + * @ignore + * + * @param int $type + * @param string $message + * @param string $file + * @param int $line + * @param array $context + */ + public function handleError($type, $message, $file, $line, $context) + { + if ($this->errorReporting() & $type) { + $error = new \ErrorException($message, 0, $type, $file, $line); + + if ($this->convertFatalErrors && ($type & (E_RECOVERABLE_ERROR | E_USER_ERROR))) { + throw $error; + } + + if ($this->logErrorTypes & $type) { + $this->log($error); + } + } + + return $this->chainedErrorHandler + ? call_user_func($this->chainedErrorHandler, $type, $message, $file, $line, $context) + : false; + } +} + diff --git a/src/ErrorHandler/HandleUncaughtException.php b/src/ErrorHandler/HandleUncaughtException.php new file mode 100644 index 0000000..7a01bb1 --- /dev/null +++ b/src/ErrorHandler/HandleUncaughtException.php @@ -0,0 +1,128 @@ +<?php + +namespace Jasny\ErrorHandler; + +/** + * Trait for handling uncaught exceptions using PHP's exception handler + */ +trait HandleUncaughtException +{ + /** + * @var callable|false + */ + protected $chainedExceptionHandler; + + + /** + * Log the following exception classes (and subclasses) + * @var array + */ + protected $logExceptionClasses = []; + + + /** + * Wrapper method for `set_error_handler` + * + * @param callable $callback + * @param int $error_types + * @return callable|null + */ + abstract protected function setErrorHandler($callback, $error_types = E_ALL); + + /** + * Wrapper method for `set_exception_handler` + * + * @param callable $callback + * @return callable|null + */ + abstract protected function setExceptionHandler($callback); + + /** + * Log an error or exception + * + * @param \Exception|\Error $error + */ + abstract public function log($error); + + + /** + * Get the error handler that has been replaced. + * + * @return callable|false|null + */ + public function getChainedExceptionHandler() + { + return $this->chainedExceptionHandler; + } + + /** + * Get a list of Exception and other Throwable classes that will be logged + * @return array + */ + public function getLoggedExceptionClasses() + { + return $this->logExceptionClasses; + } + + + /** + * Log these types of errors or exceptions + * + * @param string $class Exception class name + */ + protected function logUncaughtException($class) + { + if (!in_array($class, $this->logExceptionClasses)) { + $this->logExceptionClasses[] = $class; + } + + $this->initExceptionHandler(); + } + + + /** + * Use the global error handler + */ + protected function initExceptionHandler() + { + if (!isset($this->chainedExceptionHandler)) { + $this->chainedExceptionHandler = $this->setExceptionHandler([$this, 'handleException']) ?: false; + } + } + + /** + * Uncaught exception handler + * @ignore + * + * @param \Exception|\Error $exception + */ + public function handleException($exception) + { + $this->setExceptionHandler(null); + $this->setErrorHandler(null); + + $isInstanceOf = array_map(function($class) use ($exception) { + return is_a($exception, $class); + }, $this->logExceptionClasses); + + if ($exception instanceof \Error || $exception instanceof \ErrorException) { + $type = $exception instanceof \Error ? $exception->getCode() : $exception->getSeverity(); + $shouldLog = $this->logErrorTypes & $type; + } else { + $shouldLog = array_sum($isInstanceOf) > 0; + } + + if ($shouldLog) { + $this->log($exception); + } + + $this->callOnFatalError($exception); + + if ($this->chainedExceptionHandler) { + call_user_func($this->chainedExceptionHandler, $exception); + } + + throw $exception; // This is now handled by the default exception and error handler + } +} + diff --git a/tests/ErrorHandlerTest.php b/tests/ErrorHandlerTest.php index e6372d3..46776fc 100644 --- a/tests/ErrorHandlerTest.php +++ b/tests/ErrorHandlerTest.php @@ -11,8 +11,12 @@ use PHPUnit_Framework_MockObject_Matcher_InvokedCount as InvokedCount; /** * @covers Jasny\ErrorHandler - * @covers Jasny\ErrorHandler\ErrorCodes * @covers Jasny\ErrorHandler\Logging + * @covers Jasny\ErrorHandler\ErrorCodes + * @covers Jasny\ErrorHandler\HandleUncaughtError + * @covers Jasny\ErrorHandler\HandleShutdownError + * @covers Jasny\ErrorHandler\HandleUncaughtException + * @covers Jasny\ErrorHandler\FunctionWrapper */ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase { |