Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit 3ecc821

Browse files
committed
Moved array middleware piping logic to Application::routeMiddleware
1 parent bdd7ec7 commit 3ecc821

File tree

6 files changed

+103
-44
lines changed

6 files changed

+103
-44
lines changed

src/Application.php

+44-2
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,19 @@ public function routeMiddleware(ServerRequestInterface $request, ResponseInterfa
343343
return $middleware($request, $response, $next);
344344
}
345345

346-
if (! is_string($middleware)) {
346+
if (is_array($middleware)) {
347+
$middlewarePipe = new MiddlewarePipe();
348+
349+
foreach ($middleware as $middlewareItem) {
350+
$middlewarePipe->pipe(
351+
is_callable($middlewareItem) ? $middlewareItem : $this->marshalMiddleware($middlewareItem)
352+
);
353+
}
354+
355+
return $middlewarePipe;
356+
}
357+
358+
if (!is_string($middleware)) {
347359
throw new Exception\InvalidMiddlewareException(
348360
'The middleware specified is not callable'
349361
);
@@ -377,7 +389,7 @@ public function routeMiddleware(ServerRequestInterface $request, ResponseInterfa
377389
* pipeline.
378390
*
379391
* @param string|Router\Route $path
380-
* @param callable|string $middleware Middleware (or middleware service name) to associate with route.
392+
* @param callable|string|array $middleware Middleware (or middleware service name) to associate with route.
381393
* @param null|array $methods HTTP method to accept; null indicates any.
382394
* @param null|string $name the name of the route
383395
* @return Router\Route
@@ -531,6 +543,36 @@ private function checkForDuplicateRoute($path, $methods = null)
531543
}
532544
}
533545

546+
/**
547+
* Attempts to retrieve middleware from the container, or instantiate it directly.
548+
*
549+
* @param string $middleware
550+
*
551+
* @return array
552+
* @throws Exception\InvalidMiddlewareException If unable to obtain callable middleware
553+
*/
554+
private function marshalMiddleware($middleware)
555+
{
556+
$callable = $this->marshalMiddlewareFromContainer($middleware);
557+
558+
if (is_callable($callable)) {
559+
return $callable;
560+
}
561+
562+
$callable = $this->marshalInvokableMiddleware($middleware);
563+
564+
if (is_callable($callable)) {
565+
return $callable;
566+
} else {
567+
throw new Exception\InvalidMiddlewareException(
568+
sprintf(
569+
'Unable to resolve middleware "%s" to a callable',
570+
$middleware
571+
)
572+
);
573+
}
574+
}
575+
534576
/**
535577
* Attempt to retrieve the given middleware from the container.
536578
*

src/Container/ApplicationFactory.php

-10
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,6 @@ private function injectRoutes(Application $app, ContainerInterface $container)
179179

180180
$name = isset($spec['name']) ? $spec['name'] : null;
181181

182-
if (!is_callable($spec['middleware']) && is_array($spec['middleware'])) {
183-
$middlewarePipe = new MiddlewarePipe();
184-
185-
foreach ($spec['middleware'] as $middleware) {
186-
$middlewarePipe->pipe(is_callable($middleware) ? $middleware : $container->get($middleware));
187-
}
188-
189-
$spec['middleware'] = $middlewarePipe;
190-
}
191-
192182
$route = new Route($spec['path'], $spec['middleware'], $methods, $name);
193183

194184
if (isset($spec['options'])) {

src/Router/Route.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public function __construct($path, $middleware, $methods = self::HTTP_METHOD_ANY
7171
throw new Exception\InvalidArgumentException('Invalid path; must be a string');
7272
}
7373

74-
if (! is_callable($middleware) && ! is_string($middleware)) {
74+
if (! is_callable($middleware) && ! is_string($middleware) && ! is_array($middleware)) {
7575
throw new Exception\InvalidArgumentException('Invalid middleware; must be callable or a service name');
7676
}
7777

@@ -121,7 +121,7 @@ public function getName()
121121
}
122122

123123
/**
124-
* @return string|callable
124+
* @return string|callable|array
125125
*/
126126
public function getMiddleware()
127127
{

src/Router/RouteResult.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public function getMatchedRouteName()
127127
/**
128128
* Retrieve the matched middleware, if possible.
129129
*
130-
* @return false|callable|string Returns false if the result represents a
130+
* @return false|callable|string|array Returns false if the result represents a
131131
* failure; otherwise, a callable or a string service name.
132132
*/
133133
public function getMatchedMiddleware()

test/ApplicationTest.php

+48
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212
use PHPUnit_Framework_TestCase as TestCase;
1313
use Prophecy\Argument;
1414
use ReflectionProperty;
15+
use Zend\Diactoros\Response;
1516
use Zend\Diactoros\Response\SapiEmitter;
1617
use Zend\Diactoros\ServerRequest as Request;
18+
use Zend\Diactoros\ServerRequest;
1719
use Zend\Expressive\Application;
1820
use Zend\Expressive\Emitter\EmitterStack;
1921
use Zend\Expressive\Router\Route;
2022
use Zend\Expressive\Router\RouteResult;
2123
use Zend\Stratigility\Route as StratigilityRoute;
24+
use ZendTest\Expressive\TestAsset\InvokableMiddleware;
2225

2326
/**
2427
* @covers Zend\Expressive\Application
@@ -228,6 +231,51 @@ public function testRouteMiddlewareIsPipedAtFirstCallToRoute()
228231
$this->assertSame($routeMiddleware, $test);
229232
}
230233

234+
public function testRouteMiddlewareCanRouteArrayOfMiddlewareAsMiddlewarePipe()
235+
{
236+
$middleware = [
237+
function () {
238+
},
239+
'FooBar',
240+
[InvokableMiddleware::class, 'staticallyCallableMiddleware'],
241+
InvokableMiddleware::class,
242+
];
243+
244+
$request = new ServerRequest([], [], '/', 'GET');
245+
$routeResult = RouteResult::fromRouteMatch(__METHOD__, $middleware, []);
246+
$this->router->match($request)->willReturn($routeResult);
247+
248+
$container = $this->mockContainerInterface();
249+
$this->injectServiceInContainer($container, 'FooBar', function () {
250+
});
251+
252+
$app = new Application($this->router->reveal(), $container->reveal());
253+
$app->routeMiddleware($request, new Response(), function () {
254+
});
255+
}
256+
257+
public function uncallableMiddleware()
258+
{
259+
return [
260+
['foo'],
261+
[['foo']]
262+
];
263+
}
264+
265+
/**
266+
* @dataProvider uncallableMiddleware
267+
* @expectedException \Zend\Expressive\Exception\InvalidMiddlewareException
268+
*/
269+
public function testThrowsExceptionWhenRoutingUncallableMiddleware($middleware)
270+
{
271+
$request = new ServerRequest([], [], '/', 'GET');
272+
$routeResult = RouteResult::fromRouteMatch(__METHOD__, $middleware, []);
273+
$this->router->match($request)->willReturn($routeResult);
274+
275+
$this->getApp()->routeMiddleware($request, new Response(), function () {
276+
});
277+
}
278+
231279
public function testCannotPipeRouteMiddlewareMoreThanOnce()
232280
{
233281
$app = $this->getApp();

test/Container/ApplicationFactoryTest.php

+8-29
Original file line numberDiff line numberDiff line change
@@ -621,46 +621,25 @@ public function testExceptionIsRaisedInCaseOfInvalidRouteMethodsConfiguration()
621621
$this->factory->__invoke($this->container->reveal());
622622
}
623623

624-
public function testCanPipeRouteSpecificMiddlewareViaConfiguration()
624+
public function testExceptionIsRaisedInCaseOfInvalidRouteOptionsConfiguration()
625625
{
626-
$expectedRouteOptions = [
627-
'values' => [
628-
'foo' => 'bar'
629-
],
630-
'tokens' => [
631-
'bar' => 'foo'
632-
]
633-
];
634-
635626
$config = [
636627
'routes' => [
637628
[
638629
'path' => '/',
639-
'middleware' => [
640-
'Hello',
641-
function () {
642-
return 'World';
643-
}
644-
],
645-
'name' => 'home',
646-
'allowed_methods' => ['GET'],
647-
'options' => $expectedRouteOptions
630+
'middleware' => 'HelloWorld',
631+
'options' => 'invalid',
648632
],
649633
],
650634
];
651635

652636
$this->injectServiceInContainer($this->container, 'config', $config);
653-
$this->injectServiceInContainer($this->container, 'Hello', function() {});
654637

655-
$app = $this->factory->__invoke($this->container->reveal());
656-
657-
$r = new ReflectionProperty($app, 'routes');
658-
$r->setAccessible(true);
659-
$routes = $r->getValue($app);
660-
$route = array_shift($routes);
661-
662-
$this->assertInstanceOf(Route::class, $route);
663-
$this->assertEquals($expectedRouteOptions, $route->getOptions());
638+
$this->setExpectedException(
639+
InvalidArgumentException::class,
640+
'options must be an array; received "string"'
641+
);
642+
$this->factory->__invoke($this->container->reveal());
664643
}
665644

666645
public function testExceptionIsRaisedInCaseOfInvalidPreRoutingMiddlewarePipeline()

0 commit comments

Comments
 (0)