From b54a25804ada32c95ead9dab0c7e9e268be071f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sat, 17 Jul 2021 21:46:05 +0200 Subject: [PATCH 1/2] Added group attribute to stop router from merging exception-handlers (issue: #573) - Added new mergeExceptionHandlers attribute to stop router from merging exception-handlers. - RouteGroup: Added setMergeExceptionHandlers and getMergeExceptionHandlers methods. - IRouteGroup: Added setMergeExceptionHandlers and getMergeExceptionHandlers method. - Updated documentation to reflect changes. - Added unit-tests. --- README.md | 36 +++++++++++++++++++ src/Pecee/SimpleRouter/Route/IGroupRoute.php | 15 ++++++++ src/Pecee/SimpleRouter/Route/RouteGroup.php | 29 +++++++++++++++ src/Pecee/SimpleRouter/Router.php | 27 ++++++++------ .../Pecee/SimpleRouter/RouterRewriteTest.php | 33 +++++++++++++++-- 5 files changed, 126 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2cbf8d7..0548bf4 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c - [ExceptionHandlers](#exceptionhandlers) - [Handling 404, 403 and other errors](#handling-404-403-and-other-errors) - [Using custom exception handlers](#using-custom-exception-handlers) + - [Stop merge of parent exception-handlers](#stop-merge-of-parent-exception-handlers) - [Urls](#urls) - [Get the current url](#get-the-current-url) - [Get by name (single route)](#get-by-name-single-route) @@ -1014,6 +1015,41 @@ class CustomExceptionHandler implements IExceptionHandler } ``` +You can add your custom exception-handler class to your group by using the `exceptionHandler` settings-attribute. +`exceptionHandler` can be either class-name or array of class-names. + +```php +SimpleRouter::group(['exceptionHandler' => \Demo\Handlers\CustomExceptionHandler::class], function() { + + // Your routes here + +}); +``` + +### Stop merge of parent exception-handlers + +By default the router will merge exception-handlers to any handlers provided by parent groups, and will be executed in the order of newest to oldest. + +If you want your groups exception handler to be executed independently, you can add the `mergeExceptionHandlers` attribute and set it to `false`. + +```php +SimpleRouter::group(['prefix' => '/', 'exceptionHandler' => \Demo\Handlers\FirstExceptionHandler::class, 'mergeExceptionHandlers' => false], function() { + + SimpleRouter::group(['prefix' => '/admin', 'exceptionHandler' => \Demo\Handlers\SecondExceptionHandler::class], function() { + + // Both SecondExceptionHandler and FirstExceptionHandler will trigger (in that order). + + }); + + SimpleRouter::group(['prefix' => '/user', 'exceptionHandler' => \Demo\Handlers\SecondExceptionHandler::class, 'mergeExceptionHandlers' => false], function() { + + // Only SecondExceptionHandler will trigger. + + }); + +}); +``. + --- # Urls diff --git a/src/Pecee/SimpleRouter/Route/IGroupRoute.php b/src/Pecee/SimpleRouter/Route/IGroupRoute.php index b8cbc45..1b5da53 100644 --- a/src/Pecee/SimpleRouter/Route/IGroupRoute.php +++ b/src/Pecee/SimpleRouter/Route/IGroupRoute.php @@ -31,6 +31,21 @@ interface IGroupRoute extends IRoute */ public function setExceptionHandlers(array $handlers): self; + /** + * Returns true if group should overwrite existing exception-handlers. + * + * @return bool + */ + public function getMergeExceptionHandlers(): bool; + + /** + * When enabled group will overwrite any existing exception-handlers. + * + * @param bool $merge + * @return static + */ + public function setMergeExceptionHandlers(bool $merge): self; + /** * Get exception-handlers for group * diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index 714acc3..100a5d2 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -12,6 +12,7 @@ class RouteGroup extends Route implements IGroupRoute protected $name; protected $domains = []; protected $exceptionHandlers = []; + protected $mergeExceptionHandlers = true; /** * Method called to check if a domain matches @@ -36,6 +37,7 @@ class RouteGroup extends Route implements IGroupRoute if ($parameters !== null && count($parameters) !== 0) { $this->parameters = $parameters; + return true; } } @@ -174,6 +176,29 @@ class RouteGroup extends Route implements IGroupRoute return $this->prefix; } + /** + * When enabled group will overwrite any existing exception-handlers. + * + * @param bool $merge + * @return static + */ + public function setMergeExceptionHandlers(bool $merge): self + { + $this->mergeExceptionHandlers = $merge; + + return $this; + } + + /** + * Returns true if group should overwrite existing exception-handlers. + * + * @return bool + */ + public function getMergeExceptionHandlers(): bool + { + return $this->mergeExceptionHandlers; + } + /** * Merge with information from another route. * @@ -187,6 +212,10 @@ class RouteGroup extends Route implements IGroupRoute $this->setPrefix($settings['prefix'] . $this->prefix); } + if (isset($settings['mergeExceptionHandlers']) === true) { + $this->setMergeExceptionHandlers($settings['mergeExceptionHandlers']); + } + if ($merge === false && isset($settings['exceptionHandler']) === true) { $this->setExceptionHandlers((array)$settings['exceptionHandler']); } diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 37b979a..76721d2 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -160,7 +160,7 @@ class Router public function addRoute(IRoute $route): IRoute { $this->fireEvents(EventHandler::EVENT_ADD_ROUTE, [ - 'route' => $route, + 'route' => $route, 'isSubRoute' => $this->isProcessingRoute, ]); @@ -203,7 +203,7 @@ class Router /** * Process added routes. * - * @param array $routes + * @param array|IRoute[] $routes * @param IGroupRoute|null $group * @throws NotFoundHttpException */ @@ -211,9 +211,6 @@ class Router { $this->debug('Processing routes'); - // Loop through each route-request - $exceptionHandlers = []; - // Stop processing routes if no valid route is found. if ($this->request->getRewriteRoute() === null && $this->request->getUrl()->getOriginalUrl() === '') { $this->debug('Halted route-processing as no valid route was found'); @@ -223,7 +220,7 @@ class Router $url = $this->request->getRewriteUrl() ?? $this->request->getUrl()->getPath(); - /* @var $route IRoute */ + // Loop through each route-request foreach ($routes as $route) { $this->debug('Processing route "%s"', get_class($route)); @@ -240,13 +237,22 @@ class Router /* Add exception handlers */ if (count($route->getExceptionHandlers()) !== 0) { - /** @noinspection AdditionOperationOnArraysInspection */ - $exceptionHandlers += $route->getExceptionHandlers(); + + if ($route->getMergeExceptionHandlers() === true) { + + foreach ($route->getExceptionHandlers() as $handler) { + $this->exceptionHandlers[] = $handler; + } + + } else { + $this->exceptionHandlers = $route->getExceptionHandlers(); + } } /* Only render partial group if it matches */ if ($route instanceof IPartialGroupRoute === true) { $this->renderAndProcess($route); + continue; } } @@ -264,8 +270,6 @@ class Router $this->processedRoutes[] = $route; } } - - $this->exceptionHandlers = array_merge($exceptionHandlers, $this->exceptionHandlers); } /** @@ -679,7 +683,7 @@ class Router ->setParams($getParams); } - if($name !== null) { + if ($name !== null) { /* We try to find a match on the given name */ $route = $this->findRoute($name); @@ -942,6 +946,7 @@ class Router public function addExceptionHandler(IExceptionHandler $handler): self { $this->exceptionHandlers[] = $handler; + return $this; } diff --git a/tests/Pecee/SimpleRouter/RouterRewriteTest.php b/tests/Pecee/SimpleRouter/RouterRewriteTest.php index 285036e..a75e1d2 100644 --- a/tests/Pecee/SimpleRouter/RouterRewriteTest.php +++ b/tests/Pecee/SimpleRouter/RouterRewriteTest.php @@ -33,9 +33,9 @@ class RouterRewriteTest extends \PHPUnit\Framework\TestCase global $stack; $stack = []; - TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () use ($stack) { + TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () { - TestRouter::group(['exceptionHandler' => ExceptionHandlerThird::class], function () use ($stack) { + TestRouter::group(['prefix' => '/test', 'exceptionHandler' => ExceptionHandlerThird::class], function () { TestRouter::get('/my-path', 'DummyController@method1'); @@ -43,7 +43,7 @@ class RouterRewriteTest extends \PHPUnit\Framework\TestCase }); try { - TestRouter::debug('/my-non-existing-path', 'get'); + TestRouter::debug('/test/non-existing', 'get'); } catch (\ResponseException $e) { } @@ -58,6 +58,33 @@ class RouterRewriteTest extends \PHPUnit\Framework\TestCase } + public function testStopMergeExceptionHandlers() + { + global $stack; + $stack = []; + + TestRouter::group(['prefix' => '/', 'exceptionHandler' => ExceptionHandlerFirst::class], function () { + + TestRouter::group(['prefix' => '/admin', 'exceptionHandler' => ExceptionHandlerSecond::class, 'mergeExceptionHandlers' => false], function () { + + TestRouter::get('/my-path', 'DummyController@method1'); + + }); + }); + + try { + TestRouter::debug('/admin/my-path-test', 'get'); + } catch (\Pecee\SimpleRouter\Exceptions\NotFoundHttpException $e) { + + } + + $expectedStack = [ + ExceptionHandlerSecond::class, + ]; + + $this->assertEquals($expectedStack, $stack); + } + public function testRewriteExceptionMessage() { $this->expectException(\Pecee\SimpleRouter\Exceptions\NotFoundHttpException::class); From 0d8915b206896eca299724cbdc113c4c9660a228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sat, 17 Jul 2021 21:52:00 +0200 Subject: [PATCH 2/2] Fixed return type. --- src/Pecee/SimpleRouter/Route/RouteGroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index 100a5d2..fbd9a97 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -182,7 +182,7 @@ class RouteGroup extends Route implements IGroupRoute * @param bool $merge * @return static */ - public function setMergeExceptionHandlers(bool $merge): self + public function setMergeExceptionHandlers(bool $merge): IGroupRoute { $this->mergeExceptionHandlers = $merge;