summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnold Daniels <arnold@jasny.net>2016-11-29 11:28:38 +0100
committerArnold Daniels <arnold@jasny.net>2016-11-29 11:28:38 +0100
commitfb4bba481c8ce95749ade5a162e566eb15e6d321 (patch)
treefc454e34b5dbd8bcdc476966a799fdebf7c39cd6
parentb2c47c1a0efe0af9a3c171060258d667ce47e2a8 (diff)
downloadrouter-fb4bba481c8ce95749ade5a162e566eb15e6d321.zip
router-fb4bba481c8ce95749ade5a162e566eb15e6d321.tar.gz
router-fb4bba481c8ce95749ade5a162e566eb15e6d321.tar.bz2
Specify a path when adding middleware
-rw-r--r--src/Router.php46
-rw-r--r--tests/Router/Runner/ControllerTest.php2
-rw-r--r--tests/RouterTest.php62
3 files changed, 99 insertions, 11 deletions
diff --git a/src/Router.php b/src/Router.php
index 5cc2108..1957f91 100644
--- a/src/Router.php
+++ b/src/Router.php
@@ -94,22 +94,60 @@ class Router
}
/**
- * Add middleware call to router
+ * Add middleware call to router.
*
- * @param callback $middleware
+ * @param string $path Middleware is only applied for this path (including subdirectories), may be omitted
+ * @param callable $middleware
* @return Router $this
*/
- public function add($middleware)
+ public function add($path, $middleware = null)
{
+ if (!isset($middleware)) {
+ $middleware = $path;
+ $path = null;
+ }
+
+ if (!empty($path) && !ctype_digit($path) && $path[0] !== '/') {
+ trigger_error("Middleware path '$path' doesn't start with a '/'", E_USER_NOTICE);
+ }
+
if (!is_callable($middleware)) {
throw new \InvalidArgumentException("Middleware should be callable");
}
+ if (!empty($path)) {
+ $middleware = $this->wrapMiddleware($path, $middleware);
+ }
+
$this->middlewares[] = $middleware;
-
+
return $this;
}
+ /**
+ * Wrap middleware, so it's only applied to a specified path
+ *
+ * @param string $path
+ * @param callable $middleware
+ * @return callable
+ */
+ protected function wrapMiddleware($path, $middleware)
+ {
+ return function(
+ ServerRequestInterface $request,
+ ResponseInterface $response,
+ $next
+ ) use ($middleware, $path) {
+ $uriPath = $request->getUri()->getPath();
+
+ if ($uriPath === $path || strpos($uriPath, rtrim($path, '/') . '/') === 0) {
+ return $middleware($request, $response, $next);
+ } else {
+ return $next($request, $response);
+ }
+ };
+ }
+
/**
* Run the action for the request
diff --git a/tests/Router/Runner/ControllerTest.php b/tests/Router/Runner/ControllerTest.php
index cd3e653..c1b1801 100644
--- a/tests/Router/Runner/ControllerTest.php
+++ b/tests/Router/Runner/ControllerTest.php
@@ -10,7 +10,7 @@ use Psr\Http\Message\ResponseInterface;
use Jasny\Router\TestHelpers;
/**
- * @covers Jasny\Router\Runner\Controller;
+ * @covers Jasny\Router\Runner\Controller
*/
class ControllerTest extends \PHPUnit_Framework_TestCase
{
diff --git a/tests/RouterTest.php b/tests/RouterTest.php
index c036c71..3746574 100644
--- a/tests/RouterTest.php
+++ b/tests/RouterTest.php
@@ -1,6 +1,6 @@
<?php
-namespace Jasny\Router;
+namespace Jasny;
use Jasny\Router;
use Jasny\Router\Route;
@@ -9,6 +9,7 @@ use Jasny\Router\RunnerFactory;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
use Jasny\Router\TestHelpers;
@@ -169,7 +170,7 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$this->assertSame([$middlewareOne, $middlewareTwo], $router->getMiddlewares());
}
-
+
/**
* @expectedException \InvalidArgumentException
*/
@@ -186,6 +187,9 @@ class RouterTest extends \PHPUnit_Framework_TestCase
{
$route = $this->createMock(Route::class);
+ $uri = $this->createMock(UriInterface::class);
+ $uri->method('getPath')->willReturn('/');
+
$request = $this->createMock(ServerRequestInterface::class);
$request->expects($this->once())->method('withAttribute')->with('route')->willReturn($request);
$requestOne = $this->createMock(ServerRequestInterface::class);
@@ -197,15 +201,15 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$finalResponse = $this->createMock(ResponseInterface::class);
$middlewareOne = $this->getMockBuilder(\stdClass::class)->setMethods(['__invoke'])->getMock();
- $middlewareOne->expects($this->once())->method('__invoke')->id('one')
- ->with($request, $response, $this->isInstanceOf(Closure::class))
+ $middlewareOne->expects($this->once())->method('__invoke')
+ ->with($request, $response, $this->isInstanceOf(\Closure::class))
->will($this->returnCallback(function($a, $b, $next) use ($requestOne, $responseOne) {
return $next($requestOne, $responseOne);
}));
$middlewareTwo = $this->getMockBuilder(\stdClass::class)->setMethods(['__invoke'])->getMock();
- $middlewareTwo->expects($this->once())->method('__invoke')->id('two')->after('one')
- ->with($request, $response, $this->isInstanceOf(Closure::class))
+ $middlewareTwo->expects($this->once())->method('__invoke')
+ ->with($request, $response, $this->isInstanceOf(\Closure::class))
->will($this->returnCallback(function($a, $b, $next) use ($requestTwo, $responseTwo) {
return $next($requestTwo, $responseTwo);
}));
@@ -226,4 +230,50 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$this->assertSame($finalResponse, $result);
}
+
+ /**
+ * Test executing router with middlewares chain (test only execution order)
+ */
+ public function testRunMiddlewaresByPath()
+ {
+ $route = $this->createMock(Route::class);
+
+ $uri = $this->createMock(UriInterface::class);
+ $uri->method('getPath')->willReturn('/foo/bar');
+
+ $request = $this->createMock(ServerRequestInterface::class);
+ $request->expects($this->once())->method('withAttribute')->with('route')->willReturn($request);
+ $request->method('getUri')->willReturn($uri);
+ $requestOne = $this->createMock(ServerRequestInterface::class);
+
+ $response = $this->createMock(ResponseInterface::class);
+ $responseOne = $this->createMock(ResponseInterface::class);
+ $finalResponse = $this->createMock(ResponseInterface::class);
+
+ $middlewareOne = $this->getMockBuilder(\stdClass::class)->setMethods(['__invoke'])->getMock();
+ $middlewareOne->expects($this->once())->method('__invoke')
+ ->with($request, $response, $this->isInstanceOf(\Closure::class))
+ ->will($this->returnCallback(function($a, $b, $next) use ($requestOne, $responseOne) {
+ return $next($requestOne, $responseOne);
+ }));
+
+ $middlewareTwo = $this->getMockBuilder(\stdClass::class)->setMethods(['__invoke'])->getMock();
+ $middlewareTwo->expects($this->never())->method('__invoke');
+
+ $runner = $this->createCallbackMock($this->once(), [$requestOne, $responseOne], $finalResponse);
+ $factory = $this->createCallbackMock($this->once(), [$route], $runner);
+
+ $routes = $this->createMock(Routes::class);
+ $routes->expects($this->once())->method('getRoute')->with($request)->willReturn($route);
+
+ $router = new Router($routes);
+ $router->setFactory($factory);
+
+ $router->add('/foo', $middlewareOne);
+ $router->add('/zoo', $middlewareTwo);
+
+ $result = $router($request, $response);
+
+ $this->assertSame($finalResponse, $result);
+ }
}