From 52e0f5ef94126bcf0d6a26b086c8b4dcffdf954e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Fri, 1 Sep 2017 18:01:56 +0100 Subject: [PATCH 1/5] Removed link from documentation that has been moved. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a3df4d3..ae47b7b 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,6 @@ If you want a great new feature or experience any issues what-so-ever, please fe - [Bootmanager: loading routes dynamically](#bootmanager-loading-routes-dynamically) - [Adding routes manually](#adding-routes-manually) - [Parameters](#parameters) - - [Custom default regex for matching parameters](#custom-default-regex-for-matching-parameters) - [Extending](#extending) - [Credits](#credits) From 3a905783517b49e06502c9870af680b3a1bf73c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sun, 3 Sep 2017 16:37:20 +0100 Subject: [PATCH 2/5] Added parameter support for Group routes. --- src/Pecee/SimpleRouter/Route/IRoute.php | 2 +- src/Pecee/SimpleRouter/Route/Route.php | 3 +- src/Pecee/SimpleRouter/Route/RouteGroup.php | 18 +++++-- .../SimpleRouter/Route/RoutePartialGroup.php | 34 +++++++++++++ src/Pecee/SimpleRouter/Router.php | 51 +++++++++---------- 5 files changed, 74 insertions(+), 34 deletions(-) create mode 100644 src/Pecee/SimpleRouter/Route/RoutePartialGroup.php diff --git a/src/Pecee/SimpleRouter/Route/IRoute.php b/src/Pecee/SimpleRouter/Route/IRoute.php index 3c7d51c..e77946e 100644 --- a/src/Pecee/SimpleRouter/Route/IRoute.php +++ b/src/Pecee/SimpleRouter/Route/IRoute.php @@ -21,7 +21,7 @@ interface IRoute * * @param Request $request * @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException - * @return void + * @return string */ public function renderRoute(Request $request); diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 87ffca0..9545257 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -42,6 +42,7 @@ abstract class Route implements IRoute protected $defaultParameterRegex; protected $paramModifiers = '{}'; protected $paramOptionalSymbol = '?'; + protected $urlRegex = '/^%s\/?$/u'; protected $group; protected $parent; protected $callback; @@ -154,7 +155,7 @@ abstract class Route implements IRoute $urlRegex = preg_quote($route, '/'); } - if (preg_match('/^' . $urlRegex . '\/?$/u', $url, $matches) > 0) { + if (preg_match(sprintf($this->urlRegex, $urlRegex), $url, $matches) > 0) { $values = []; diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index e6397b1..d15c343 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -48,9 +48,17 @@ class RouteGroup extends Route implements IGroupRoute */ public function matchRoute($url, Request $request) { - /* Skip if prefix doesn't match */ - if ($this->prefix !== null && stripos($url, $this->prefix) === false) { - return false; + if($this->prefix !== null) { + /* Parse parameters from current route */ + $parameters = $this->parseParameters($this->prefix, $url); + + /* If no custom regular expression or parameters was found on this route, we stop */ + if ($parameters === null) { + return false; + } + + /* Set the parameters */ + $this->setParameters((array)$parameters); } return $this->matchDomain($request); @@ -150,11 +158,11 @@ class RouteGroup extends Route implements IGroupRoute $this->setPrefix($values['prefix'] . $this->prefix); } - if (isset($values['exceptionHandler'])) { + if ($merge === false && isset($values['exceptionHandler'])) { $this->setExceptionHandlers((array)$values['exceptionHandler']); } - if (isset($values['domain'])) { + if ($merge === false &&isset($values['domain'])) { $this->setDomains((array)$values['domain']); } diff --git a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php new file mode 100644 index 0000000..12613c4 --- /dev/null +++ b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php @@ -0,0 +1,34 @@ +prefix !== null) { + /* Parse parameters from current route */ + $parameters = $this->parseParameters($this->prefix, $url); + + /* If no custom regular expression or parameters was found on this route, we stop */ + if ($parameters === null) { + return false; + } + + /* Set the parameters */ + $this->setParameters((array)$parameters); + } + + return $this->matchDomain($request); + } + +} \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index ebe7672..5fd5824 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -129,15 +129,27 @@ class Router $route = $routes[$i]; + if ($parent !== null) { + + /* Add the parent route */ + $route->setParent($parent); + + /* Add/merge parent settings with child */ + $route->setSettings($parent->toArray(), true); + + } + + if ($group !== null) { + + /* Add the parent group */ + $route->setGroup($group); + } + /* @var $route IGroupRoute */ if ($route instanceof IGroupRoute) { $group = $route; - $this->processingRoute = true; - $route->renderRoute($this->request); - $this->processingRoute = false; - if ($route->matchRoute($url, $this->request) === true) { /* Add exception handlers */ @@ -147,22 +159,10 @@ class Router } } - } - - if ($group !== null) { - - /* Add the parent group */ - $route->setGroup($group); - } - - if ($parent !== null) { - - /* Add the parent route */ - $route->setParent($parent); - - /* Add/merge parent settings with child */ - $route->setSettings($parent->toArray(), true); + $this->processingRoute = true; + $route->renderRoute($this->request); + $this->processingRoute = false; } if ($route instanceof ILoadableRoute) { @@ -258,9 +258,7 @@ class Router if ($rewriteUrl !== null && $rewriteUrl !== $url) { unset($this->processedRoutes[$i]); $this->processedRoutes = array_values($this->processedRoutes); - $this->routeRequest(true); - - return; + return $this->routeRequest(true); } /* Render route */ @@ -268,8 +266,6 @@ class Router $this->request->setLoadedRoute($route); return $route->renderRoute($this->request); - - break; } } @@ -293,12 +289,15 @@ class Router $this->handleException(new NotFoundHttpException($message, 404)); } + + return null; } /** * @param \Exception $e * @throws HttpException * @throws \Exception + * @return string */ protected function handleException(\Exception $e) { @@ -335,9 +334,7 @@ class Router if ($rewriteUrl !== null && $rewriteUrl !== $url) { unset($this->exceptionHandlers[$i]); $this->exceptionHandlers = array_values($this->exceptionHandlers); - $this->routeRequest(true); - - return; + return $this->routeRequest(true); } } } From 7f924c7d0ae5c88715db893d0d845fca4c2751f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sun, 3 Sep 2017 16:49:15 +0100 Subject: [PATCH 3/5] Added RouterGroupTest unit-tests. --- src/Pecee/SimpleRouter/Route/Route.php | 4 +-- src/Pecee/SimpleRouter/Route/RouteGroup.php | 4 +-- src/Pecee/SimpleRouter/Router.php | 2 ++ test/RouterGroupTest.php | 29 +++++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 test/RouterGroupTest.php diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 9545257..1e04dde 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -70,7 +70,7 @@ abstract class Route implements IRoute $callback = $this->getCallback(); if ($callback === null) { - return; + return null; } /* Render callback function */ @@ -162,7 +162,7 @@ abstract class Route implements IRoute if (isset($parameters[1]) === true) { /* Only take matched parameters with name */ - foreach ($parameters[1] as $name) { + foreach ((array)$parameters[1] as $name) { $values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null; } } diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index d15c343..d4dd64d 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -48,7 +48,7 @@ class RouteGroup extends Route implements IGroupRoute */ public function matchRoute($url, Request $request) { - if($this->prefix !== null) { + if ($this->prefix !== null) { /* Parse parameters from current route */ $parameters = $this->parseParameters($this->prefix, $url); @@ -162,7 +162,7 @@ class RouteGroup extends Route implements IGroupRoute $this->setExceptionHandlers((array)$values['exceptionHandler']); } - if ($merge === false &&isset($values['domain'])) { + if ($merge === false && isset($values['domain'])) { $this->setDomains((array)$values['domain']); } diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 5fd5824..02837fc 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -258,6 +258,7 @@ class Router if ($rewriteUrl !== null && $rewriteUrl !== $url) { unset($this->processedRoutes[$i]); $this->processedRoutes = array_values($this->processedRoutes); + return $this->routeRequest(true); } @@ -334,6 +335,7 @@ class Router if ($rewriteUrl !== null && $rewriteUrl !== $url) { unset($this->exceptionHandlers[$i]); $this->exceptionHandlers = array_values($this->exceptionHandlers); + return $this->routeRequest(true); } } diff --git a/test/RouterGroupTest.php b/test/RouterGroupTest.php new file mode 100644 index 0000000..ff806d7 --- /dev/null +++ b/test/RouterGroupTest.php @@ -0,0 +1,29 @@ + '{param1}/{param2}'], function ($param1 = null, $param2 = null) use (&$result1, &$result2) { + $result1 = $param1; + $result2 = $param2; + + TestRouter::get('/', 'DummyController@method1'); + }); + + TestRouter::debug('/param1/param2', 'get'); + + $this->assertEquals('param1', $result1); + $this->assertEquals('param2', $result2); + } + +} \ No newline at end of file From a5aac57ce94eaf9112765ed30d36168ad2aade39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sun, 3 Sep 2017 18:24:51 +0100 Subject: [PATCH 4/5] Added RoutePartialGroup support. --- README.md | 26 +++++++++++++++-- .../SimpleRouter/Route/IPartialGroupRoute.php | 8 ++++++ src/Pecee/SimpleRouter/Route/RouteGroup.php | 14 ++-------- .../SimpleRouter/Route/RoutePartialGroup.php | 6 ++-- src/Pecee/SimpleRouter/Router.php | 16 +++++++++-- src/Pecee/SimpleRouter/SimpleRouter.php | 28 +++++++++++++++++++ ...oupTest.php => RouterPartialGroupTest.php} | 4 +-- 7 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 src/Pecee/SimpleRouter/Route/IPartialGroupRoute.php rename test/{RouterGroupTest.php => RouterPartialGroupTest.php} (75%) diff --git a/README.md b/README.md index ae47b7b..2d59d25 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ If you want a great new feature or experience any issues what-so-ever, please fe - [Namespaces](#namespaces) - [Subdomain-routing](#subdomain-routing) - [Route prefixes](#route-prefixes) + - [Partial groups](#partial-groups) - [Form Method Spoofing](#form-method-spoofing) - [Accessing The Current Route](#accessing-the-current-route) - [Other examples](#other-examples) @@ -357,7 +358,7 @@ SimpleRouter::any('foo', function() { }); ``` -We've created a simple method which matches `GET` and `POST` which is most commenly used: +We've created a simple method which matches `GET` and `POST` which is most commonly used: ```php SimpleRouter::form('foo', function() { @@ -541,7 +542,7 @@ SimpleRouter::group(['namespace' => 'Admin'], function () { }); ``` -### Subdomain-routing +### Sub domain-routing Route groups may also be used to handle sub-domain routing. Sub-domains may be assigned route parameters just like route URIs, allowing you to capture a portion of the sub-domain for usage in your route or controller. The sub-domain may be specified using the `domain` key on the group attribute array: @@ -565,6 +566,27 @@ SimpleRouter::group(['prefix' => '/admin'], function () { }); ``` +## Partial groups + +Partial router groups has the same benefits as a normal group, but supports parameters and are only rendered when the url is matched. + +This can be extremely useful in situations where you only want certain routes added when certain criteria has been met. + +**NOTE:** Use partial groups with caution as routes added within are only rendered and available once the url of the partial-group has matched. This can cause `url()` not to find urls for the routes added within. + +**Example:** + +```php +SimpleRouter::partialGroup('/admin/{applicationId}', function ($applicationId) { + + // Routes within are rendered when the group matches + + // The url "/admin/{applicationId}" will be prepended to any routes added here. + +}); +``` + + ## Form Method Spoofing HTML forms do not support `PUT`, `PATCH` or `DELETE` actions. So, when defining `PUT`, `PATCH` or `DELETE` routes that are called from an HTML form, you will need to add a hidden `_method` field to the form. The value sent with the `_method` field will be used as the HTTP request method: diff --git a/src/Pecee/SimpleRouter/Route/IPartialGroupRoute.php b/src/Pecee/SimpleRouter/Route/IPartialGroupRoute.php new file mode 100644 index 0000000..c1ddd4d --- /dev/null +++ b/src/Pecee/SimpleRouter/Route/IPartialGroupRoute.php @@ -0,0 +1,8 @@ +prefix !== null) { - /* Parse parameters from current route */ - $parameters = $this->parseParameters($this->prefix, $url); - - /* If no custom regular expression or parameters was found on this route, we stop */ - if ($parameters === null) { - return false; - } - - /* Set the parameters */ - $this->setParameters((array)$parameters); + /* Skip if prefix doesn't match */ + if ($this->prefix !== null && stripos($url, $this->prefix) === false) { + return false; } return $this->matchDomain($request); diff --git a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php index 12613c4..3790c0d 100644 --- a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php +++ b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php @@ -4,8 +4,10 @@ namespace Pecee\SimpleRouter\Route; use Pecee\Http\Request; -class PartialGroup extends RouteGroup +class RoutePartialGroup extends RouteGroup implements IPartialGroupRoute { + protected $urlRegex = '/^%s\/?/u'; + /** * Method called to check if route matches * @@ -15,7 +17,7 @@ class PartialGroup extends RouteGroup */ public function matchRoute($url, Request $request) { - if($this->prefix !== null) { + if ($this->prefix !== null) { /* Parse parameters from current route */ $parameters = $this->parseParameters($this->prefix, $url); diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 02837fc..8144a95 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -10,6 +10,7 @@ use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; use Pecee\SimpleRouter\Route\IControllerRoute; use Pecee\SimpleRouter\Route\IGroupRoute; use Pecee\SimpleRouter\Route\ILoadableRoute; +use Pecee\SimpleRouter\Route\IPartialGroupRoute; use Pecee\SimpleRouter\Route\IRoute; class Router @@ -158,11 +159,20 @@ class Router $exceptionHandlers += $route->getExceptionHandlers(); } + /* Only render partial group if it matches */ + if ($route instanceof IPartialGroupRoute) { + $this->processingRoute = true; + $route->renderRoute($this->request); + $this->processingRoute = false; + } + } - $this->processingRoute = true; - $route->renderRoute($this->request); - $this->processingRoute = false; + if (($route instanceof IPartialGroupRoute) === false) { + $this->processingRoute = true; + $route->renderRoute($this->request); + $this->processingRoute = false; + } } if ($route instanceof ILoadableRoute) { diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index 84734d3..2c545a2 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -16,6 +16,7 @@ use Pecee\Http\Response; use Pecee\SimpleRouter\Exceptions\HttpException; use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; use Pecee\SimpleRouter\Route\IRoute; +use Pecee\SimpleRouter\Route\RoutePartialGroup; use Pecee\SimpleRouter\Route\RouteController; use Pecee\SimpleRouter\Route\RouteGroup; use Pecee\SimpleRouter\Route\RouteResource; @@ -171,14 +172,41 @@ class SimpleRouter */ public static function group(array $settings = [], \Closure $callback) { + if (is_callable($callback) === false) { + throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported'); + } + $group = new RouteGroup(); $group->setCallback($callback); $group->setSettings($settings); + static::router()->addRoute($group); + + return $group; + } + + /** + * Special group that has the same benefits as group but supports + * parameters and which are only rendered when the url matches. + * + * @param string $url + * @param array $settings + * @param \Closure $callback + * @throws \InvalidArgumentException + * @return RoutePartialGroup + */ + public static function partialGroup($url, \Closure $callback, array $settings = []) + { if (is_callable($callback) === false) { throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported'); } + $settings['prefix'] = $url; + + $group = new RoutePartialGroup(); + $group->setSettings($settings); + $group->setCallback($callback); + static::router()->addRoute($group); return $group; diff --git a/test/RouterGroupTest.php b/test/RouterPartialGroupTest.php similarity index 75% rename from test/RouterGroupTest.php rename to test/RouterPartialGroupTest.php index ff806d7..9c805e5 100644 --- a/test/RouterGroupTest.php +++ b/test/RouterPartialGroupTest.php @@ -5,7 +5,7 @@ require_once 'Dummy/DummyController.php'; require_once 'Dummy/Handler/ExceptionHandler.php'; require_once 'Helpers/TestRouter.php'; -class RouterGroupTest extends PHPUnit_Framework_TestCase +class RouterPartialGroupTest extends PHPUnit_Framework_TestCase { public function testParameters() @@ -13,7 +13,7 @@ class RouterGroupTest extends PHPUnit_Framework_TestCase $result1 = null; $result2 = null; - TestRouter::group(['prefix' => '{param1}/{param2}'], function ($param1 = null, $param2 = null) use (&$result1, &$result2) { + TestRouter::partialGroup('{param1}/{param2}', function ($param1 = null, $param2 = null) use (&$result1, &$result2) { $result1 = $param1; $result2 = $param2; From 98ad310404feb63a83afd2512a351a0c562a6b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sun, 3 Sep 2017 18:36:41 +0100 Subject: [PATCH 5/5] Updated documentation. --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2d59d25..3375724 100644 --- a/README.md +++ b/README.md @@ -568,9 +568,9 @@ SimpleRouter::group(['prefix' => '/admin'], function () { ## Partial groups -Partial router groups has the same benefits as a normal group, but supports parameters and are only rendered when the url is matched. +Partial router groups has the same benefits as a normal group, but supports parameters and are only rendered once the url has matched. -This can be extremely useful in situations where you only want certain routes added when certain criteria has been met. +This can be extremely useful in situations, where you only want special routes to be added, when a certain criteria or logic has been met. **NOTE:** Use partial groups with caution as routes added within are only rendered and available once the url of the partial-group has matched. This can cause `url()` not to find urls for the routes added within. @@ -579,9 +579,11 @@ This can be extremely useful in situations where you only want certain routes ad ```php SimpleRouter::partialGroup('/admin/{applicationId}', function ($applicationId) { - // Routes within are rendered when the group matches + SimpleRouter::get('/', function($applicationId) { - // The url "/admin/{applicationId}" will be prepended to any routes added here. + // Matches The "/admin/applicationId" URL + + }); }); ```