diff options
author | Arnold Daniels <arnold@jasny.net> | 2016-10-28 00:18:36 +0200 |
---|---|---|
committer | Arnold Daniels <arnold@jasny.net> | 2016-10-28 00:18:36 +0200 |
commit | 8bf861bf03eb6ea98d0af861a835e6cb29406078 (patch) | |
tree | 78f6327c5289ccd845dab63e284bbf658152bdc8 | |
parent | 414bce100e6fcc913bdb79fb21c553ebdea3a51e (diff) | |
download | error-handler-8bf861bf03eb6ea98d0af861a835e6cb29406078.zip error-handler-8bf861bf03eb6ea98d0af861a835e6cb29406078.tar.gz error-handler-8bf861bf03eb6ea98d0af861a835e6cb29406078.tar.bz2 |
Added `onFatalError` method
This allows you to set a callback for when the script dies because of a fatal error
-rw-r--r-- | src/ErrorHandler.php | 40 | ||||
-rw-r--r-- | tests/ErrorHandlerTest.php | 72 |
2 files changed, 101 insertions, 11 deletions
diff --git a/src/ErrorHandler.php b/src/ErrorHandler.php index 1f24c53..799c45e 100644 --- a/src/ErrorHandler.php +++ b/src/ErrorHandler.php @@ -53,6 +53,11 @@ class ErrorHandler implements LoggerAwareInterface */ protected $reservedMemory; + /** + * @var callback + */ + protected $onFatalError; + /** * Set the logger for logging errors @@ -216,6 +221,7 @@ class ErrorHandler implements LoggerAwareInterface return $errorResponse; } + /** * Use the global error handler to convert E_USER_ERROR and E_RECOVERABLE_ERROR to an ErrorException */ @@ -247,6 +253,25 @@ class ErrorHandler implements LoggerAwareInterface } /** + * Set a callback for when the script dies because of a fatal, non-catchable error. + * The callback should have an `ErrorException` as only argument. + * + * @param callable $callback + * @param boolean $clearOutput Clear the output buffer before calling the callback + */ + public function onFatalError($callback, $clearOutput = false) + { + if (!$clearOutput) { + $this->onFatalError = $callback; + } else { + $this->onFatalError = function($error) use ($callback) { + $this->clearOutputBuffer(); + $callback($error); + }; + } + } + + /** * Use the global error handler */ protected function initErrorHandler() @@ -326,6 +351,10 @@ class ErrorHandler implements LoggerAwareInterface if ($err['type'] & $this->logErrorTypes) { $this->log($error); } + + if ($this->onFatalError) { + call_user_func($this->onFatalError, $error); + } } @@ -404,6 +433,17 @@ class ErrorHandler implements LoggerAwareInterface /** + * Clear and destroy all the output buffers + * @codeCoverageIgnore + */ + protected function clearOutputBuffer() + { + while (ob_get_level() > 0) { + ob_end_clean(); + } + } + + /** * Wrapper method for `error_reporting` * @codeCoverageIgnore * diff --git a/tests/ErrorHandlerTest.php b/tests/ErrorHandlerTest.php index e8f3523..a49b3ad 100644 --- a/tests/ErrorHandlerTest.php +++ b/tests/ErrorHandlerTest.php @@ -25,7 +25,8 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase public function setUp() { $this->errorHandler = $this->getMockBuilder(ErrorHandler::class) - ->setMethods(['errorReporting', 'errorGetLast', 'setErrorHandler', 'registerShutdownFunction']) + ->setMethods(['errorReporting', 'errorGetLast', 'setErrorHandler', 'registerShutdownFunction', + 'clearOutputBuffer']) ->getMock(); } @@ -383,12 +384,12 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase * * @param int $alsoLog * @param int $code - * @param InvokedCount $expectsLog + * @param InvokedCount $expectLog */ - public function testHandleErrorWithLogging($alsoLog, $code, InvokedCount $expectsLog) + public function testHandleErrorWithLogging($alsoLog, $code, InvokedCount $expectLog) { $logger = $this->createMock(LoggerInterface::class); - $logger->expects($expectsLog)->method('log') + $logger->expects($expectLog)->method('log') ->with($this->isType('string'), $this->stringEndsWith("no good at foo.php line 42"), $this->anything()); $errorHandler = $this->errorHandler; @@ -405,10 +406,10 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase * * @param int $alsoLog Ignored * @param int $code - * @param InvokedCount $expectsLog Ignored + * @param InvokedCount $expectLog Ignored * @param boolean $expectException */ - public function testHandleErrorWithConvertError($alsoLog, $code, InvokedCount $expectsLog, $expectException) + public function testHandleErrorWithConvertError($alsoLog, $code, InvokedCount $expectLog, $expectException) { $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->never())->method('log'); @@ -453,14 +454,14 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase /** * @dataProvider shutdownFunctionProvider * - * @param int $alsoLog Ignored + * @param int $alsoLog Ignored * @param int $code - * @param InvokedCount $expectsLog Ignored + * @param InvokedCount $expectLog Ignored */ - public function testShutdownFunction($alsoLog, $code, InvokedCount $expectsLog) + public function testShutdownFunction($alsoLog, $code, InvokedCount $expectLog) { $logger = $this->createMock(LoggerInterface::class); - $logger->expects($expectsLog)->method('log') + $logger->expects($expectLog)->method('log') ->with($this->isType('string'), $this->stringEndsWith("no good at foo.php line 42"), $this->anything()); $errorHandler = $this->errorHandler; @@ -478,6 +479,55 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase $errorHandler->setLogger($logger); $errorHandler->alsoLog($alsoLog); - $this->errorHandler->shutdownFunction(); + $this->assertAttributeNotEmpty('reservedMemory', $errorHandler); + + $errorHandler->shutdownFunction(); + + $this->assertAttributeEmpty('reservedMemory', $errorHandler); + } + + public function shutdownFunctionWithCallbackProvider() + { + return [ + [true, $this->once()], + [false, $this->never()] + ]; + } + + /** + * @dataProvider shutdownFunctionWithCallbackProvider + * + * @param boolean $clearOutput + * @param InvokedCount $expectClear + */ + public function testShutdownFunctionWithCallback($clearOutput, InvokedCount $expectClear) + { + $errorHandler = $this->errorHandler; + + $error = [ + 'type' => E_ERROR, + 'message' => 'no good', + 'file' => 'foo.php', + 'line' => 42 + ]; + + $errorHandler->expects($this->once())->method('errorGetLast')->willReturn($error); + + $errorHandler->expects($expectClear)->method('clearOutputBuffer'); + + $callback = $this->getMockBuilder(\stdClass::class)->setMethods(['__invoke'])->getMock(); + $callback->expects($this->once())->method('__invoke') + ->with($this->callback(function($error){ + $this->assertInstanceOf(\ErrorException::class, $error); + $this->assertEquals('no good', $error->getMessage()); + $this->assertEquals('foo.php', $error->getFile()); + $this->assertEquals(42, $error->getLine()); + + return true; + })); + + $errorHandler->onFatalError($callback, $clearOutput); + + $errorHandler->shutdownFunction(); } } |