Merge pull request #290 from skipperbent/v3-development

Version 3.4.5
This commit is contained in:
Simon Sessingø
2017-09-03 19:43:09 +02:00
committed by GitHub
9 changed files with 168 additions and 34 deletions
+26 -3
View File
@@ -43,6 +43,7 @@ If you want a great new feature or experience any issues what-so-ever, please fe
- [Namespaces](#namespaces) - [Namespaces](#namespaces)
- [Subdomain-routing](#subdomain-routing) - [Subdomain-routing](#subdomain-routing)
- [Route prefixes](#route-prefixes) - [Route prefixes](#route-prefixes)
- [Partial groups](#partial-groups)
- [Form Method Spoofing](#form-method-spoofing) - [Form Method Spoofing](#form-method-spoofing)
- [Accessing The Current Route](#accessing-the-current-route) - [Accessing The Current Route](#accessing-the-current-route)
- [Other examples](#other-examples) - [Other examples](#other-examples)
@@ -79,7 +80,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) - [Bootmanager: loading routes dynamically](#bootmanager-loading-routes-dynamically)
- [Adding routes manually](#adding-routes-manually) - [Adding routes manually](#adding-routes-manually)
- [Parameters](#parameters) - [Parameters](#parameters)
- [Custom default regex for matching parameters](#custom-default-regex-for-matching-parameters)
- [Extending](#extending) - [Extending](#extending)
- [Credits](#credits) - [Credits](#credits)
@@ -358,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 ```php
SimpleRouter::form('foo', function() { SimpleRouter::form('foo', function() {
@@ -542,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: 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:
@@ -566,6 +566,29 @@ 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 once the url has matched.
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.
**Example:**
```php
SimpleRouter::partialGroup('/admin/{applicationId}', function ($applicationId) {
SimpleRouter::get('/', function($applicationId) {
// Matches The "/admin/applicationId" URL
});
});
```
## Form Method Spoofing ## 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: 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:
@@ -0,0 +1,8 @@
<?php
namespace Pecee\SimpleRouter\Route;
interface IPartialGroupRoute
{
}
+1 -1
View File
@@ -21,7 +21,7 @@ interface IRoute
* *
* @param Request $request * @param Request $request
* @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException * @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException
* @return void * @return string
*/ */
public function renderRoute(Request $request); public function renderRoute(Request $request);
+4 -3
View File
@@ -42,6 +42,7 @@ abstract class Route implements IRoute
protected $defaultParameterRegex; protected $defaultParameterRegex;
protected $paramModifiers = '{}'; protected $paramModifiers = '{}';
protected $paramOptionalSymbol = '?'; protected $paramOptionalSymbol = '?';
protected $urlRegex = '/^%s\/?$/u';
protected $group; protected $group;
protected $parent; protected $parent;
protected $callback; protected $callback;
@@ -69,7 +70,7 @@ abstract class Route implements IRoute
$callback = $this->getCallback(); $callback = $this->getCallback();
if ($callback === null) { if ($callback === null) {
return; return null;
} }
/* Render callback function */ /* Render callback function */
@@ -154,14 +155,14 @@ abstract class Route implements IRoute
$urlRegex = preg_quote($route, '/'); $urlRegex = preg_quote($route, '/');
} }
if (preg_match('/^' . $urlRegex . '\/?$/u', $url, $matches) > 0) { if (preg_match(sprintf($this->urlRegex, $urlRegex), $url, $matches) > 0) {
$values = []; $values = [];
if (isset($parameters[1]) === true) { if (isset($parameters[1]) === true) {
/* Only take matched parameters with name */ /* 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; $values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
} }
} }
+2 -2
View File
@@ -150,11 +150,11 @@ class RouteGroup extends Route implements IGroupRoute
$this->setPrefix($values['prefix'] . $this->prefix); $this->setPrefix($values['prefix'] . $this->prefix);
} }
if (isset($values['exceptionHandler'])) { if ($merge === false && isset($values['exceptionHandler'])) {
$this->setExceptionHandlers((array)$values['exceptionHandler']); $this->setExceptionHandlers((array)$values['exceptionHandler']);
} }
if (isset($values['domain'])) { if ($merge === false && isset($values['domain'])) {
$this->setDomains((array)$values['domain']); $this->setDomains((array)$values['domain']);
} }
@@ -0,0 +1,36 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RoutePartialGroup extends RouteGroup implements IPartialGroupRoute
{
protected $urlRegex = '/^%s\/?/u';
/**
* Method called to check if route matches
*
* @param string $url
* @param Request $request
* @return bool
*/
public function matchRoute($url, Request $request)
{
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);
}
}
+34 -25
View File
@@ -10,6 +10,7 @@ use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\IControllerRoute; use Pecee\SimpleRouter\Route\IControllerRoute;
use Pecee\SimpleRouter\Route\IGroupRoute; use Pecee\SimpleRouter\Route\IGroupRoute;
use Pecee\SimpleRouter\Route\ILoadableRoute; use Pecee\SimpleRouter\Route\ILoadableRoute;
use Pecee\SimpleRouter\Route\IPartialGroupRoute;
use Pecee\SimpleRouter\Route\IRoute; use Pecee\SimpleRouter\Route\IRoute;
class Router class Router
@@ -129,15 +130,27 @@ class Router
$route = $routes[$i]; $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 */ /* @var $route IGroupRoute */
if ($route instanceof IGroupRoute) { if ($route instanceof IGroupRoute) {
$group = $route; $group = $route;
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
if ($route->matchRoute($url, $this->request) === true) { if ($route->matchRoute($url, $this->request) === true) {
/* Add exception handlers */ /* Add exception handlers */
@@ -146,23 +159,20 @@ class Router
$exceptionHandlers += $route->getExceptionHandlers(); $exceptionHandlers += $route->getExceptionHandlers();
} }
/* Only render partial group if it matches */
if ($route instanceof IPartialGroupRoute) {
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
}
} }
}
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);
if (($route instanceof IPartialGroupRoute) === false) {
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
}
} }
if ($route instanceof ILoadableRoute) { if ($route instanceof ILoadableRoute) {
@@ -258,9 +268,8 @@ class Router
if ($rewriteUrl !== null && $rewriteUrl !== $url) { if ($rewriteUrl !== null && $rewriteUrl !== $url) {
unset($this->processedRoutes[$i]); unset($this->processedRoutes[$i]);
$this->processedRoutes = array_values($this->processedRoutes); $this->processedRoutes = array_values($this->processedRoutes);
$this->routeRequest(true);
return; return $this->routeRequest(true);
} }
/* Render route */ /* Render route */
@@ -268,8 +277,6 @@ class Router
$this->request->setLoadedRoute($route); $this->request->setLoadedRoute($route);
return $route->renderRoute($this->request); return $route->renderRoute($this->request);
break;
} }
} }
@@ -293,12 +300,15 @@ class Router
$this->handleException(new NotFoundHttpException($message, 404)); $this->handleException(new NotFoundHttpException($message, 404));
} }
return null;
} }
/** /**
* @param \Exception $e * @param \Exception $e
* @throws HttpException * @throws HttpException
* @throws \Exception * @throws \Exception
* @return string
*/ */
protected function handleException(\Exception $e) protected function handleException(\Exception $e)
{ {
@@ -335,9 +345,8 @@ class Router
if ($rewriteUrl !== null && $rewriteUrl !== $url) { if ($rewriteUrl !== null && $rewriteUrl !== $url) {
unset($this->exceptionHandlers[$i]); unset($this->exceptionHandlers[$i]);
$this->exceptionHandlers = array_values($this->exceptionHandlers); $this->exceptionHandlers = array_values($this->exceptionHandlers);
$this->routeRequest(true);
return; return $this->routeRequest(true);
} }
} }
} }
+28
View File
@@ -16,6 +16,7 @@ use Pecee\Http\Response;
use Pecee\SimpleRouter\Exceptions\HttpException; use Pecee\SimpleRouter\Exceptions\HttpException;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\IRoute; use Pecee\SimpleRouter\Route\IRoute;
use Pecee\SimpleRouter\Route\RoutePartialGroup;
use Pecee\SimpleRouter\Route\RouteController; use Pecee\SimpleRouter\Route\RouteController;
use Pecee\SimpleRouter\Route\RouteGroup; use Pecee\SimpleRouter\Route\RouteGroup;
use Pecee\SimpleRouter\Route\RouteResource; use Pecee\SimpleRouter\Route\RouteResource;
@@ -171,14 +172,41 @@ class SimpleRouter
*/ */
public static function group(array $settings = [], \Closure $callback) 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 = new RouteGroup();
$group->setCallback($callback); $group->setCallback($callback);
$group->setSettings($settings); $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) { if (is_callable($callback) === false) {
throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported'); 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); static::router()->addRoute($group);
return $group; return $group;
+29
View File
@@ -0,0 +1,29 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
require_once 'Helpers/TestRouter.php';
class RouterPartialGroupTest extends PHPUnit_Framework_TestCase
{
public function testParameters()
{
$result1 = null;
$result2 = null;
TestRouter::partialGroup('{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);
}
}