Merge pull request #367 from skipperbent/v3-rewrite-update

Simplified url-rewriting and callback handling.
This commit is contained in:
Simon Sessingø
2018-02-24 05:36:37 +01:00
committed by GitHub
6 changed files with 208 additions and 168 deletions

116
README.md
View File

@@ -1136,125 +1136,18 @@ $siteId = input('site_id', 2, ['post', 'get']);
## Url rewriting
Sometimes it can be useful to manipulate the route about to be loaded.
simple-php-router allows you to easily change the route about to be executed.
simple-php-router allows you to easily manipulate and change the routes which are about to be rendered.
All information about the current route is stored in the `\Pecee\SimpleRouter\Router` instance's `loadedRoute` property.
For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::router()`.
For easy access you can use the shortcut helper function `request()` instead of calling the class directly `\Pecee\SimpleRouter\SimpleRouter::router()`.
```php
use Pecee\SimpleRouter;
$request = SimpleRouter::request();
$request->setRewriteCallback('Example\MyCustomClass@hello');
request()->setRewriteCallback('Example\MyCustomClass@hello');
// -- or you can rewrite by url --
$request->setRewriteUrl('/my-rewrite-url');
```
**Note:** It's only possible to change the route BEFORE the route has initially been rendered. You can use the `Request` object to manipulate the route which are about to be loaded.
### Rewrite using callback
This method is most efficient, as it will render the route immediately.
This method is useful for rendering 404-pages etc.
You can also change the callback by modifying the `$route` parameter. This is perfect if you just want to display a view quickly - or change the callback depending
on some criteria's for the request.
The callback below will fire immediately after the `Middleware` or `ExceptionHandler` has been loaded, as they are loaded before the route is rendered.
If you wish to change the callback from outside, please have this in mind.
The example below will render `DefaultController@notFound` regardless of the url.
**NOTE: Use this method if you want to load another controller. No additional middlewares or rules will be loaded.**
##### Middleware example
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class CustomMiddleware implements IMiddleware {
public function handle(Request $request) {
$request->setRewriteCallback('Demo\Controllers\DefaultController@notFound');
return $request;
}
}
```
##### Exception handler example
```php
namespace Demo\Handlers;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, \Exception $error)
{
/* The router will throw the NotFoundHttpException on 404 */
if($error instanceof NotFoundHttpException) {
/*
* Render your own custom 404-view, rewrite the request to another route,
* or simply return the $request object to ignore the error and continue on rendering the route.
*
* The code below will make the router render our page.notfound route.
*/
$request->setRewriteCallback('Demo\Controllers\DefaultController@notFound');
return $request;
}
throw $error;
}
}
```
### Rewrite using url
The example below will cause the router to reload the request and reinitialize all the routes. This method is slower, but will ensure that all middlewares and rules for the route is loaded.
This method is useful if you want to redirect a url to another-url which is dependent on a middleware. You can also add a custom rule by calling `$request->setRewriteRoute($route)` if
you want to customize request-methods or use another route-type like `RouteController` etc.
We are using the `url()` helper function to get the uri to another route added in the `routes.php` file.
**NOTE: Use this method if you want to fully load another route using it's settings (request method, middlewares etc).**
##### Middleware example
The example below will redirect the request to the `home`-route.
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class CustomMiddleware implements IMiddleware {
public function handle(Request $request) {
$request->setRewriteUrl(url('home'));
return $request;
}
}
request()->setRewriteUrl('/my-rewrite-url');
```
### Bootmanager: loading routes dynamically
@@ -1282,7 +1175,6 @@ class CustomRouterRules implement IRouterBootManager {
if($request->getUri()->getPath() === $url) {
$request->setRewriteUrl($rule);
return $request;
}
}

View File

@@ -16,6 +16,8 @@ class Request
protected $method;
protected $input;
protected $hasRewrite = false;
/**
* @var ILoadableRoute|null
*/
@@ -231,6 +233,7 @@ class Request
*/
public function setRewriteRoute(ILoadableRoute $route)
{
$this->hasRewrite = true;
$this->rewriteRoute = SimpleRouter::addDefaultNamespace($route);
return $this;
@@ -264,7 +267,8 @@ class Request
*/
public function setRewriteUrl($rewriteUrl)
{
$this->rewriteUrl = $rewriteUrl;
$this->hasRewrite = true;
$this->rewriteUrl = rtrim($rewriteUrl, '/') . '/';
return $this;
}
@@ -276,7 +280,9 @@ class Request
*/
public function setRewriteCallback($callback)
{
return $this->setRewriteRoute(new RouteUrl($this->uri, $callback));
$this->hasRewrite = true;
return $this->setRewriteRoute(new RouteUrl($this->getUri()->getPath(), $callback));
}
/**
@@ -301,6 +307,23 @@ class Request
return $this;
}
public function hasRewrite()
{
return $this->hasRewrite;
}
public function setHasRewrite($value)
{
$this->hasRewrite = $value;
return $this;
}
public function isRewrite($url)
{
return ($this->rewriteUrl === $url);
}
public function __isset($name)
{
return array_key_exists($name, $this->data);

View File

@@ -128,7 +128,7 @@ abstract class Route implements IRoute
// Ensures that hostnames/domains will work with parameters
$url = '/' . ltrim($url, '/');
if (preg_match_all('/' . $regex . '/u', $route, $parameters) > 0) {
if (preg_match_all('/' . $regex . '/u', $route, $parameters) !== 0) {
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/', $route);
@@ -166,22 +166,21 @@ abstract class Route implements IRoute
$urlRegex = preg_quote($route, '/');
}
if (preg_match(sprintf($this->urlRegex, $urlRegex), $url, $matches) > 0) {
$values = [];
if (isset($parameters[1]) === true) {
/* Only take matched parameters with name */
foreach ((array)$parameters[1] as $name) {
$values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
}
}
return $values;
if (preg_match(sprintf($this->urlRegex, $urlRegex), $url, $matches) === 0) {
return null;
}
return null;
$values = [];
if (isset($parameters[1]) === true) {
/* Only take matched parameters with name */
foreach ((array)$parameters[1] as $name) {
$values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
}
}
return $values;
}
/**

View File

@@ -224,6 +224,8 @@ class Router
/* Verify csrf token for request */
$this->csrfVerifier->handle($this->request);
}
} else {
$this->request->setHasRewrite(false);
}
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
@@ -231,7 +233,6 @@ class Router
/* @var $route ILoadableRoute */
foreach ($this->processedRoutes as $key => $route) {
/* If the route matches */
if ($route->matchRoute($url, $this->request) === true) {
@@ -243,29 +244,28 @@ class Router
$route->loadMiddleware($this->request);
$rewriteRoute = $this->request->getRewriteRoute();
if ($rewriteRoute !== null) {
$rewriteRoute->loadMiddleware($this->request);
return $rewriteRoute->renderRoute($this->request);
}
/* If the request has changed */
$rewriteUrl = $this->request->getRewriteUrl();
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
if ($this->hasRewrite($url) === true) {
unset($this->processedRoutes[$key]);
$this->processedRoutes = array_values($this->processedRoutes);
return $this->routeRequest(true);
}
/* Render route */
$routeNotAllowed = false;
$this->request->setLoadedRoute($route);
return $route->renderRoute($this->request);
$output = $route->renderRoute($this->request);
if ($output !== null) {
return $output;
}
if ($this->hasRewrite($url) === true) {
unset($this->processedRoutes[$key]);
return $this->routeRequest(true);
}
}
}
@@ -294,6 +294,31 @@ class Router
return null;
}
protected function hasRewrite($url)
{
/* If the request has changed */
if ($this->request->hasRewrite() === true) {
if ($this->request->getRewriteRoute() !== null) {
/* Render rewrite-route */
$this->processedRoutes[] = $this->request->getRewriteRoute();
return true;
}
if ($this->request->isRewrite($url) === false) {
/* Render rewrite-url */
$this->processedRoutes = array_values($this->processedRoutes);
return true;
}
}
return false;
}
/**
* @param \Exception $e
* @throws HttpException
@@ -302,8 +327,6 @@ class Router
*/
protected function handleException(\Exception $e)
{
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
/* @var $handler IExceptionHandler */
foreach ($this->exceptionHandlers as $key => $handler) {
@@ -317,25 +340,13 @@ class Router
try {
if ($handler->handleError($this->request, $e) !== null) {
$handler->handleError($this->request, $e);
$rewriteRoute = $this->request->getRewriteRoute();
if ($this->request->hasRewrite() === true) {
unset($this->exceptionHandlers[$key]);
$this->exceptionHandlers = array_values($this->exceptionHandlers);
if ($rewriteRoute !== null) {
$rewriteRoute->loadMiddleware($this->request);
return $rewriteRoute->renderRoute($this->request);
}
$rewriteUrl = $this->request->getRewriteUrl();
/* If the request has changed */
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
unset($this->exceptionHandlers[$key]);
$this->exceptionHandlers = array_values($this->exceptionHandlers);
return $this->routeRequest(true);
}
return $this->routeRequest(true);
}
} catch (\Exception $e) {

View File

@@ -0,0 +1,16 @@
<?php
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class RewriteMiddleware implements IMiddleware {
public function handle(Request $request) {
$request->setRewriteCallback(function() {
return 'ok';
});
}
}

View File

@@ -5,6 +5,7 @@ require_once 'Dummy/Handler/ExceptionHandlerFirst.php';
require_once 'Dummy/Handler/ExceptionHandlerSecond.php';
require_once 'Dummy/Handler/ExceptionHandlerThird.php';
require_once 'Helpers/TestRouter.php';
require_once 'Dummy/Middlewares/RewriteMiddleware.php';
class RouteRewriteTest extends PHPUnit_Framework_TestCase
{
@@ -33,9 +34,9 @@ class RouteRewriteTest extends PHPUnit_Framework_TestCase
global $stack;
$stack = [];
TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () {
TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () use ($stack) {
TestRouter::group(['exceptionHandler' => ExceptionHandlerThird::class], function () {
TestRouter::group(['exceptionHandler' => ExceptionHandlerThird::class], function () use ($stack) {
TestRouter::get('/my-path', 'DummyController@method1');
@@ -64,10 +65,8 @@ class RouteRewriteTest extends PHPUnit_Framework_TestCase
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) {
if (strtolower($request->getUri()->getPath()) == '/my/test') {
if (strtolower($request->getUri()->getPath()) === '/my/test/') {
$request->setRewriteUrl('/another-non-existing');
return $request;
}
});
@@ -75,4 +74,104 @@ class RouteRewriteTest extends PHPUnit_Framework_TestCase
TestRouter::debug('/my/test', 'get');
}
public function testRewriteUrlFromRoute()
{
TestRouter::get('/old', function () {
TestRouter::request()->setRewriteUrl('/new');
});
TestRouter::get('/new', function () {
echo 'ok';
});
TestRouter::get('/new1', function () {
echo 'ok';
});
TestRouter::get('/new2', function () {
echo 'ok';
});
$output = TestRouter::debugOutput('/old');
$this->assertEquals('ok', $output);
}
public function testRewriteCallbackFromRoute()
{
TestRouter::get('/old', function () {
TestRouter::request()->setRewriteUrl('/new');
});
TestRouter::get('/new', function () {
return 'ok';
});
TestRouter::get('/new1', function () {
return 'fail';
});
TestRouter::get('/new/2', function () {
return 'fail';
});
$output = TestRouter::debugOutput('/old');
TestRouter::router()->reset();
$this->assertEquals('ok', $output);
}
public function testRewriteRouteFromRoute()
{
TestRouter::get('/match', function () {
TestRouter::request()->setRewriteRoute(new \Pecee\SimpleRouter\Route\RouteUrl('/match', function () {
return 'ok';
}));
});
TestRouter::get('/old1', function () {
return 'fail';
});
TestRouter::get('/old/2', function () {
return 'fail';
});
TestRouter::get('/new2', function () {
return 'fail';
});
$output = TestRouter::debugOutput('/match');
TestRouter::router()->reset();
$this->assertEquals('ok', $output);
}
public function testMiddlewareRewrite()
{
TestRouter::group(['middleware' => 'RewriteMiddleware'], function () {
TestRouter::get('/', function () {
return 'fail';
});
TestRouter::get('no/match', function () {
return 'fail';
});
});
$output = TestRouter::debugOutput('/');
$this->assertEquals('ok', $output);
}
}