summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnold Daniels <arnold@jasny.net>2017-01-03 22:27:47 +0100
committerArnold Daniels <arnold@jasny.net>2017-01-03 22:27:47 +0100
commitac34b91af334058bede75652f1872e75a0fff623 (patch)
tree399dab583411452adda670e095ec4f4ad2d3944c
parent63ec91cda3794f07da8eb8b0ef4aaff08e342a08 (diff)
downloaderror-handler-ac34b91af334058bede75652f1872e75a0fff623.zip
error-handler-ac34b91af334058bede75652f1872e75a0fff623.tar.gz
error-handler-ac34b91af334058bede75652f1872e75a0fff623.tar.bz2
Split up ErrorHandler class into traits
-rw-r--r--src/ErrorHandler.php337
-rw-r--r--src/ErrorHandler/FunctionWrapper.php65
-rw-r--r--src/ErrorHandler/HandleShutdownError.php96
-rw-r--r--src/ErrorHandler/HandleUncaughtError.php154
-rw-r--r--src/ErrorHandler/HandleUncaughtException.php128
-rw-r--r--tests/ErrorHandlerTest.php6
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
{