From 3d6ad48a11e7afdab4e29707a1be1be683454568 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Mon, 16 May 2022 01:44:26 +0300 Subject: [PATCH 1/3] Added Support for Registering Multiple Handlers + Added Support for Handling Errors at Shutdown --- ...ceptionHandler.php => AbstractHandler.php} | 52 +++++++++++- ...ceptionsHandler.php => DefaultHandler.php} | 11 ++- src/webfiori/error/Handler.php | 84 ++++++++++++++++--- tests/bootstrap.php | 4 +- 4 files changed, 137 insertions(+), 14 deletions(-) rename src/webfiori/error/{AbstractExceptionHandler.php => AbstractHandler.php} (63%) rename src/webfiori/error/{DefaultExceptionsHandler.php => DefaultHandler.php} (83%) diff --git a/src/webfiori/error/AbstractExceptionHandler.php b/src/webfiori/error/AbstractHandler.php similarity index 63% rename from src/webfiori/error/AbstractExceptionHandler.php rename to src/webfiori/error/AbstractHandler.php index 5e4994c..7e12a47 100644 --- a/src/webfiori/error/AbstractExceptionHandler.php +++ b/src/webfiori/error/AbstractHandler.php @@ -7,14 +7,53 @@ * * @author Ibrahim */ -abstract class AbstractExceptionHandler { +abstract class AbstractHandler { private $exception; private $traceArr; + private $name; + private $isCalled; /** * Creates new instance of the class. */ public function __construct() { $this->traceArr = []; + $this->name = 'New Handler'; + $this->isCalled = false; + } + /** + * Sets the handler as executed. + * + * This method is used to make sure that same handler won't get executed twice. + * + * @param bool $bool True to set it as executed, false to not. + */ + public function setIsExecuted(bool $bool) { + $this->isCalled = $bool; + } + /** + * Checks if the handler was executed once or not. + * + * @return bool If the method returned true, then this means the handler + * was executed. + */ + public function isExecuted() : bool { + return $this->isCalled; + } + /** + * Gives the handler a specific name. + * + * @param string $name The custom name of the handler. + */ + public function setName(string $name) { + $this->name = trim($name); + } + /** + * Returns the name of the handler. + * + * @return string The name of the handler. + */ + public function getName() : string { + return $this->name; } /** * Returns a string that represents the name of the class that an exception @@ -65,6 +104,17 @@ public function getTrace() : array { * The developer can implement this method to handle all thrown exceptions. */ public abstract function handle(); + /** + * Checks if the handler will be used to handle errors or not. + * + * The developer must implement this method in a way it returns true if the + * handler will get executed. False otherwise. + */ + public abstract function isActive() : bool; + /** + * Checks if the handler will be called in case of error after shutdown. + */ + public abstract function isShutdownHandler() : bool; /** * Sets the exception which was thrown by an error on the code. * diff --git a/src/webfiori/error/DefaultExceptionsHandler.php b/src/webfiori/error/DefaultHandler.php similarity index 83% rename from src/webfiori/error/DefaultExceptionsHandler.php rename to src/webfiori/error/DefaultHandler.php index 49131e8..57a0d27 100644 --- a/src/webfiori/error/DefaultExceptionsHandler.php +++ b/src/webfiori/error/DefaultHandler.php @@ -9,7 +9,7 @@ * * @author Ibrahim */ -class DefaultExceptionsHandler extends AbstractExceptionHandler { +class DefaultHandler extends AbstractHandler { /** * Creates new instance of the class. */ @@ -38,4 +38,13 @@ public function handle() { } echo ''; } + + public function isActive(): bool { + return true; + } + + public function isShutdownHandler(): bool { + return true; + } + } diff --git a/src/webfiori/error/Handler.php b/src/webfiori/error/Handler.php index 8be5a69..2a0acc7 100644 --- a/src/webfiori/error/Handler.php +++ b/src/webfiori/error/Handler.php @@ -8,6 +8,7 @@ * @author Ibrahim */ class Handler { + private $handlersPool; /** * An array which holds one constant that is used to hold the meanings of different * PHP errors. @@ -78,7 +79,7 @@ class Handler { ]; /** * - * @var AbstractExceptionHandler + * @var AbstractHandler */ private $handler; /** @@ -99,11 +100,36 @@ private function __construct() { }); set_exception_handler(function (Throwable $ex) { - $class = Handler::get()->handler; - $class->setException($ex); - $class->handle(); + foreach (Handler::get()->handlersPool as $h) { + + if ($h->isActive()) { + $h->setException($ex); + $h->handle(); + $h->setIsExecuted(true); + } + } }); - $this->handler = new DefaultExceptionsHandler(); + register_shutdown_function(function () { + $lastErr = error_get_last(); + + if ($lastErr !== null) { + ob_clean(); + $errClass = TraceEntry::extractClassName($lastErr['file']); + $errType = Handler::ERR_TYPES[$lastErr['type']]; + $message = $errType['description'].': '.$lastErr['message'].' At '.$errClass.' Line '.$lastErr['line']; + $ex = new ErrorHandlerException($message, $lastErr['type'], $lastErr['file']); + foreach (Handler::get()->handlersPool as $h) { + + if ($h->isActive() && $h->isShutdownHandler() && !$h->isExecuted()) { + $h->setException($ex); + $h->handle(); + $h->setIsExecuted(true); + } + } + } + }); + $this->handlersPool = []; + $this->handlersPool[] = new DefaultHandler(); } /** * Returns the instance which is used to handle exceptions and errors. @@ -120,19 +146,57 @@ public static function get() { /** * Sets a custom handler to handle exceptions. * - * @param AbstractExceptionHandler $h A class that implements a custom + * @param AbstractHandler $h A class that implements a custom + * handler. + */ + public static function registerHandler(AbstractHandler $h) { + if (!self::hasHandler($h->getName())) { + self::get()->handlersPool[] = $h; + } + } + /** + * Remove a registered errors handler. + * + * @param AbstractHandler $h A class that implements a custom * handler. */ - public static function setHandler(AbstractExceptionHandler $h) { - self::get()->handler = $h; + public static function unregisterHandler(AbstractHandler $h) : bool { + $tempPool = []; + $removed = false; + foreach (self::get()->handlersPool as $handler) { + if ($handler->getName() != $h->getName()) { + $tempPool[] = $handler; + continue; + } + $removed = true; + } + self::get()->handlersPool = $tempPool; + return $removed; + } + /** + * Checks if a handler is registered or not given its name. + * + * @param string $name The name of the handler. + * + * @return bool If such handler is registered, the method will return true. + * Other than that, the method will return false. + */ + public static function hasHandler(string $name) : bool { + $trimmed = trim($name); + foreach (self::get()->handlersPool as $handler) { + if ($handler->getName() == $trimmed) { + return true; + } + } + return false; } /** * Returns the handler instance which is used to handle exceptions. * - * @return AbstractExceptionHandler The handler instance which is used to + * @return AbstractHandler The handler instance which is used to * handle exceptions. */ - public function getHandler() : AbstractExceptionHandler { + public function getHandler() : AbstractHandler { return self::get()->handler; } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1c6beaa..3fdef71 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -21,8 +21,8 @@ $classesPath = $rootDir.'src'.DS.'webfiori'.DS.'error'.DS; require_once $classesPath . 'ErrorHandlerException.php'; -require_once $classesPath . 'AbstractExceptionHandler.php'; -require_once $classesPath . 'DefaultExceptionsHandler.php'; +require_once $classesPath . 'AbstractHandler.php'; +require_once $classesPath . 'DefaultHandler.php'; require_once $classesPath . 'TraceEntry.php'; require_once $classesPath . 'Handler.php'; From 906950b33b14b177beb52316b40c21b948928860 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Mon, 16 May 2022 01:49:44 +0300 Subject: [PATCH 2/3] Update Handler.php --- src/webfiori/error/Handler.php | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/webfiori/error/Handler.php b/src/webfiori/error/Handler.php index 2a0acc7..db3c800 100644 --- a/src/webfiori/error/Handler.php +++ b/src/webfiori/error/Handler.php @@ -173,6 +173,26 @@ public static function unregisterHandler(AbstractHandler $h) : bool { self::get()->handlersPool = $tempPool; return $removed; } + /** + * Returns a handler given its name. + * + * @param string $name The name of the handler. + * + * @return AbstractHandler|null If a handler which has the given name is found, + * it will be returned as an object. Other than that, null is returned. + */ + public static function &getHandler(string $name) { + $h = null; + $trimmed = trim($name); + + foreach (self::get()->handlersPool as $handler) { + if ($handler->getName() == $trimmed) { + $h = $handler; + break; + } + } + return $h; + } /** * Checks if a handler is registered or not given its name. * @@ -190,13 +210,4 @@ public static function hasHandler(string $name) : bool { } return false; } - /** - * Returns the handler instance which is used to handle exceptions. - * - * @return AbstractHandler The handler instance which is used to - * handle exceptions. - */ - public function getHandler() : AbstractHandler { - return self::get()->handler; - } } From dc5211d953c203de83b3666c5874c8d6e4794935 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Mon, 16 May 2022 01:49:48 +0300 Subject: [PATCH 3/3] Update DefaultHandler.php --- src/webfiori/error/DefaultHandler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webfiori/error/DefaultHandler.php b/src/webfiori/error/DefaultHandler.php index 57a0d27..4f2a4b3 100644 --- a/src/webfiori/error/DefaultHandler.php +++ b/src/webfiori/error/DefaultHandler.php @@ -15,6 +15,7 @@ class DefaultHandler extends AbstractHandler { */ public function __construct() { parent::__construct(); + $this->setName('Default'); } /** * Handles the exception.