diff options
author | minstel <minstel@yandex.ru> | 2016-10-12 13:33:42 +0300 |
---|---|---|
committer | minstel <minstel@yandex.ru> | 2016-10-12 13:33:42 +0300 |
commit | f9df2a343453c80ac982cb46af60c7b40ace74fc (patch) | |
tree | 91b3a426fa45f5542753a9758d35851758183796 | |
parent | 353ef30d54dc23135480c720473011d97ee3132e (diff) | |
download | router-f9df2a343453c80ac982cb46af60c7b40ace74fc.zip router-f9df2a343453c80ac982cb46af60c7b40ace74fc.tar.gz router-f9df2a343453c80ac982cb46af60c7b40ace74fc.tar.bz2 |
Add and run middlewares in Router
-rw-r--r-- | src/Router.php | 53 | ||||
-rw-r--r-- | tests/RouterTest.php | 112 |
2 files changed, 152 insertions, 13 deletions
diff --git a/src/Router.php b/src/Router.php index 33ff6d0..955e1e1 100644 --- a/src/Router.php +++ b/src/Router.php @@ -17,6 +17,12 @@ class Router * @var array */ protected $routes = []; + + /** + * Middlewares actions + * @var array + **/ + protected $middlewares = []; /** * Class constructor @@ -37,6 +43,33 @@ class Router { return $this->routes; } + + /** + * Get middlewares + * + * @return array + */ + public function getMiddlewares() + { + return $this->middlewares; + } + + /** + * Add middleware call to router + * + * @param callback $middleware + * @return Router $this + */ + public function add($middleware) + { + if (!is_callable($middleware)) { + throw new \InvalidArgumentException("Middleware should be a callable"); + } + + $this->middlewares[] = $middleware; + + return $this; + } /** * Run the action for the request @@ -51,16 +84,30 @@ class Router } /** - * Run the action for the request (optionally as middleware) + * Run the action for the request (optionally as middleware), previously running middlewares, if any * * @param ServerRequestInterface $request * @param ResponseInterface $response - * @param callback $next + * @param callback $next * @return ResponseInterface */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) { - return $this->handle($request, $response, $next); + $handle = [$this, 'handle']; + + #Call to $this->handle will be executed last in the chain of middlewares + $next = function(ServerRequestInterface $request, ResponseInterface $response) use ($next, $handle) { + return call_user_func($handle, $request, $response, $next); + }; + + #Build middlewares call chain, so that the last added was executed in first place + foreach ($this->middlewares as $middleware) { + $next = function(ServerRequestInterface $request, ResponseInterface $response) use ($next, $middleware) { + return $middleware($request, $response, $next); + }; + } + + return $next($request, $response); } /** diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 889f9c1..9b63360 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -19,7 +19,7 @@ class RouterTest extends PHPUnit_Framework_TestCase ]; $router = new Router($routes); - $this->assertEquals($routes, $router->getRoutes()); + $this->assertEquals($routes, $router->getRoutes(), "Routes were not set correctly"); } /** @@ -28,8 +28,7 @@ class RouterTest extends PHPUnit_Framework_TestCase public function testRun() { $router = $this->createMock(Router::class, ['__invoke']); - $request = $this->createMock(ServerRequestInterface::class); - $response = $this->createMock(ResponseInterface::class); + list($request, $response) = $this->getRequests(); $router->method('__invoke')->will($this->returnCallback(function($arg1, $arg2) { return ['request' => $arg1, 'response' => $arg2]; @@ -37,8 +36,8 @@ class RouterTest extends PHPUnit_Framework_TestCase $result = $router->run($request, $response); - $this->assertEquals($request, $result['request']); - $this->assertEquals($response, $result['response']); + $this->assertEquals($request, $result['request'], "Request was not processed correctly"); + $this->assertEquals($response, $result['response'], "Response was not processed correctly"); } /** @@ -57,8 +56,8 @@ class RouterTest extends PHPUnit_Framework_TestCase $router = new Router($routes); $result = $router($request, $response); - $this->assertEquals($request, $result['request']); - $this->assertEquals($response, $result['response']); + $this->assertEquals($request, $result['request'], "Request was not processed correctly"); + $this->assertEquals($response, $result['response'], "Response was not processed correctly"); } /** @@ -79,8 +78,8 @@ class RouterTest extends PHPUnit_Framework_TestCase return ['request' => $arg1, 'response' => $arg2]; }); - $this->assertEquals($request, $result['request']); - $this->assertEquals($response, $result['response']); + $this->assertEquals($request, $result['request'], "Request was not processed correctly"); + $this->assertEquals($response, $result['response'], "Response was not processed correctly"); } /** @@ -98,7 +97,72 @@ class RouterTest extends PHPUnit_Framework_TestCase $router = new Router($routes); $result = $router($request, $response); - $this->assertEquals(get_class($response), get_class($result)); + $this->assertEquals(get_class($response), get_class($result), "Returned result is not an instance of 'ServerRequestInterface'"); + } + + /** + * Test adding middleware action + * + * @dataProvider addProvider + * @param mixed $middleware1 + * @param callable $middleware2 + * @param boolean $positive + */ + public function testAdd($middleware1, $middleware2, $positive) + { + $router = new Router([]); + $this->assertEquals(0, count($router->getMiddlewares()), "Middlewares array should be empty"); + + if (!$positive) $this->expectException(\InvalidArgumentException::class); + + $result = $router->add($middleware1); + $this->assertEquals(1, count($router->getMiddlewares()), "There should be only one item in middlewares array"); + $this->assertEquals($middleware1, reset($router->getMiddlewares()), "Wrong item in middlewares array"); + $this->assertEquals($router, $result, "'Add' should return '\$this'"); + + if (!$middleware2) return; + + $router->add($middleware2); + $this->assertEquals(2, count($router->getMiddlewares()), "There should be two items in middlewares array"); + foreach ($router->getMiddlewares() as $action) { + $this->assertTrue($action == $middleware1 || $action == $middleware2, "Wrong item in middlewares array"); + } + } + + /** + * Provide data for testing 'add' method + */ + public function addProvider() + { + return [ + ['wrong_callback', null, false], + [[$this, 'getMiddlewareCalledFirst'], null, true], + [[$this, 'getMiddlewareCalledFirst'], [$this, 'getMiddlewareCalledLast'], true] + ]; + } + + /** + * Test executing router with middlewares chain (test only execution order) + */ + public function testRunMiddlewares() + { + $routes = [ + '/foo' => Route::create(['fn' => function($request, $response) { + $response->testMiddlewareCalls[] = 'handle'; + return $response; + }]) + ]; + + list($request, $response) = $this->getRequests(); + $router = new Router($routes); + $router->add([$this, 'getMiddlewareCalledLast'])->add([$this, 'getMiddlewareCalledFirst']); + + $result = $router($request, $response, function($request, $response) { + $response->testMiddlewareCalls[] = 'outer'; + return $response; + }); + + $this->assertEquals(['first','last','handle','outer'], $response->testMiddlewareCalls, "Actions were executed in wrong order"); } /** @@ -118,6 +182,34 @@ class RouterTest extends PHPUnit_Framework_TestCase } /** + * Get middleware action, that should ba called first in middleware chain + * + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param callback $next + * @return ResponseInterface + */ + public function getMiddlewareCalledFirst(ServerRequestInterface $request, ResponseInterface $response, $next) + { + $response->testMiddlewareCalls[] = 'first'; + return $next($request, $response); + } + + /** + * Get middleware action, that should be called last in middleware chain + * + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param callback $next + * @return ResponseInterface + */ + public function getMiddlewareCalledLast(ServerRequestInterface $request, ResponseInterface $response, $next) + { + $response->testMiddlewareCalls[] = 'last'; + return $next($request, $response); + } + + /** * Expect 'not found' response * * @param ResponseInterface |