diff --git a/phpunit.xml b/phpunit.xml index 2735502..365b49a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -5,6 +5,8 @@ ./src/webfiori/error/TraceEntry.php + ./src/webfiori/error/Handler.php + ./src/webfiori/error/AbstractHandler.php diff --git a/tests/webfiori/tests/error/HandlerTest.php b/tests/webfiori/tests/error/HandlerTest.php index 06815a1..de68889 100644 --- a/tests/webfiori/tests/error/HandlerTest.php +++ b/tests/webfiori/tests/error/HandlerTest.php @@ -2,10 +2,11 @@ namespace webfiori\tests\error; -namespace webfiori\tests\error; +require_once 'SampleHandler1.php'; +require_once 'SampleHandler2.php'; use PHPUnit\Framework\TestCase; -use webfiori\error\TraceEntry; +use webfiori\error\ErrorHandlerException; use webfiori\error\Handler; /** * Description of HandlerTest @@ -17,7 +18,44 @@ class HandlerTest extends TestCase { * @test */ public function test00() { + $this->expectException(ErrorHandlerException::class); + $this->expectExceptionMessage('An exception caused by an error. Run-time warning: Undefined variable $y at HandlerTest Line 24'); + $h = Handler::get(); + $x = $y; + } + /** + * @test + */ + public function test01() { + $h = Handler::get(); + $this->assertFalse($h->hasHandler('New Handler')); + $h->registerHandler(new SampleHandler1()); + $this->assertTrue($h->hasHandler('New Handler')); + $h->unregisterHandler($h->getHandler('New Handler')); + $this->assertFalse($h->hasHandler('New Handler')); + } + /** + * @test + */ + public function test02() { + $h = Handler::get(); + $h->reset(); + $h->registerHandler(new SampleHandler1()); + $this->assertFalse(defined('SampleHandler1')); + $h->invokExceptionHandler(); + $this->assertTrue(defined('SampleHandler1')); + } + /** + * @test + */ + public function test03() { $h = Handler::get(); - $this->assertTrue(true); + $h->reset(); + $h->registerHandler(new SampleHandler2()); + $this->assertFalse(defined('SampleHandler2')); + $h->invokExceptionHandler(); + $this->assertFalse(defined('SampleHandler2')); + $h->invokShutdownHandler(); + $this->assertTrue(defined('SampleHandler2')); } } diff --git a/tests/webfiori/tests/error/SampleHandler1.php b/tests/webfiori/tests/error/SampleHandler1.php new file mode 100644 index 0000000..740c330 --- /dev/null +++ b/tests/webfiori/tests/error/SampleHandler1.php @@ -0,0 +1,23 @@ +getException()->getFile()); + return TraceEntry::extractClassName($this->getException() !== null ? $this->getException()->getFile() : 'X'); } /** * Returns exception error code. @@ -43,9 +43,9 @@ public function getCode() : string { /** * Returns an object that represents the exception which was thrown. * - * @return Throwable An object that represents the exception which was thrown. + * @return Throwable|null An object that represents the exception which was thrown. */ - public function getException() : Throwable { + public function getException() { return $this->exception; } /** @@ -54,7 +54,7 @@ public function getException() : Throwable { * @return string The number of line at which the exception was thrown at. */ public function getLine() : string { - return $this->getException()->getLine().''; + return $this->getException() !== null ? $this->getException()->getLine().'' : 'X'; } /** * Returns a string that represents exception message. @@ -62,7 +62,7 @@ public function getLine() : string { * @return string A string that represents exception message. */ public function getMessage() : string { - return $this->getException()->getMessage(); + return $this->getException() !== null ? $this->getException()->getMessage() : 'No Message'; } /** * Returns the name of the handler. @@ -116,6 +116,9 @@ public function isExecuting() : bool { } /** * Checks if the handler will be called in case of error after shutdown. + * + * Note that if the handler is set as shutdown handler, it will not + * get executed during normal events. */ public abstract function isShutdownHandler() : bool; /** diff --git a/webfiori/error/Handler.php b/webfiori/error/Handler.php index 4569500..92bec28 100644 --- a/webfiori/error/Handler.php +++ b/webfiori/error/Handler.php @@ -1,6 +1,7 @@ isErrOccured = false; - set_error_handler(function (int $errno, string $errString, string $errFile, int $errLine) - { + $this->errToExceptionHandler = function (int $errno, string $errString, string $errFile, int $errLine) { + //Convert errors to exceptions $errClass = TraceEntry::extractClassName($errFile); $errType = Handler::ERR_TYPES[$errno]; $message = 'An exception caused by an error. '.$errType['description'].': '.$errString.' at '.$errClass.' Line '.$errLine; throw new ErrorHandlerException($message, $errno, $errFile); - }); - set_exception_handler(function (Throwable $ex) - { - $this->lastException = $ex; + }; + $this->exceptionsHandler = function (Throwable $ex = null) { + Handler::get()->lastException = $ex; foreach (Handler::get()->handlersPool as $h) { - if ($h->isActive()) { - $h->setException($ex); + if ($h->isActive() && !$h->isShutdownHandler()) { + if ($ex !== null) { + $h->setException($ex); + } $h->setIsExecuting(true); $h->handle(); $h->setIsExecuting(false); $h->setIsExecuted(true); } } - }); - register_shutdown_function(function () - { + }; + $this->shutdownFunction = function () { if ($this->lastException !== null) { if (ob_get_length()) { ob_clean(); @@ -133,10 +136,28 @@ private function __construct() { } } } - }); + }; + $this->isErrOccured = false; + set_exception_handler($this->exceptionsHandler); + set_error_handler($this->errToExceptionHandler); + + register_shutdown_function($this->shutdownFunction); $this->handlersPool = []; $this->handlersPool[] = new DefaultHandler(); } + public function invokShutdownHandler() { + self::get()->lastException = new Exception(); + call_user_func(self::get()->shutdownFunction); + } + public function invokExceptionHandler() { + call_user_func(self::get()->exceptionsHandler); + } + public static function reset() { + $h = self::get(); + $h->handlersPool = []; + $h->handlersPool[] = new DefaultHandler(); + set_error_handler($h->errToExceptionHandler); + } /** * Returns a handler given its name. *