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

Commit 4d5fb79

Browse files
committed
Allow using PSR-15 request handlers
This patch updates the `MiddlewareContainer` and `MiddlewareFactory` to allow them to detect PSR-15 request handlers, and decorate them using zend-stratigility's `RequestHandlerMiddleware`. This ensures type-safety when used in either pipelines or with routed middleware, but also ensures we can use the full-spectrum of available handlers with the system. A new method was also added to `MiddlewareFactory`: `handler()`. This method accepts a `RequestHandlerInterface` instance and returns a `RequestHandlerMiddleware` instance decorating it, making it simple to use request handlers with the explicit functionality of zend-expressive-router's `PathBasedRoutingMiddleware` API.
1 parent 9db3610 commit 4d5fb79

File tree

8 files changed

+101
-31
lines changed

8 files changed

+101
-31
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"zendframework/zend-expressive-router": "^3.0.0alpha1",
3232
"zendframework/zend-expressive-template": "^2.0.0-dev",
3333
"zendframework/zend-httphandlerrunner": "^1.0",
34-
"zendframework/zend-stratigility": "^3.0.0alpha2"
34+
"zendframework/zend-stratigility": "3.0.0alpha3"
3535
},
3636
"require-dev": {
3737
"filp/whoops": "^1.1.10 || ^2.1.13",

composer.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Application.php

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,12 @@ public function run() : void
9292
*
9393
* The resulting middleware, in both cases, is piped to the pipeline.
9494
*
95-
* @param string|array|callable|MiddlewareInterface $middlewareOrPath
95+
* @param string|array|callable|MiddlewareInterface|RequestHandlerInterface $middlewareOrPath
9696
* Either the middleware to pipe, or the path to segregate the $middleware
9797
* by, via a PathMiddlewareDecorator.
98-
* @param null|string|array|callable|MiddlewareInterface $middleware If present,
99-
* middleware to segregate by the path specified in $middlewareOrPath.
98+
* @param null|string|array|callable|MiddlewareInterface|RequestHandlerInterface $middleware
99+
* If present, middleware or request handler to segregate by the path
100+
* specified in $middlewareOrPath.
100101
*/
101102
public function pipe($middlewareOrPath, $middleware = null) : void
102103
{
@@ -113,8 +114,9 @@ public function pipe($middlewareOrPath, $middleware = null) : void
113114
/**
114115
* Add a route for the route middleware to match.
115116
*
116-
* @param string|array|callable|MiddlewareInterface $middleware Middleware
117-
* (or middleware service name) to associate with route.
117+
* @param string|array|callable|MiddlewareInterface|RequestHandlerInterface $middleware
118+
* Middleware or request handler (or service name resolving to one of
119+
* those types) to associate with route.
118120
* @param null|array $methods HTTP method to accept; null indicates any.
119121
* @param null|string $name The name of the route.
120122
*/
@@ -129,8 +131,9 @@ public function route(string $path, $middleware, array $methods = null, string $
129131
}
130132

131133
/**
132-
* @param string|array|callable|MiddlewareInterface $middleware Middleware
133-
* (or middleware service name) to associate with route.
134+
* @param string|array|callable|MiddlewareInterface|RequestHandlerInterface $middleware
135+
* Middleware or request handler (or service name resolving to one of
136+
* those types) to associate with route.
134137
* @param null|string $name The name of the route.
135138
*/
136139
public function get(string $path, $middleware, string $name = null) : Router\Route
@@ -139,8 +142,9 @@ public function get(string $path, $middleware, string $name = null) : Router\Rou
139142
}
140143

141144
/**
142-
* @param string|array|callable|MiddlewareInterface $middleware Middleware
143-
* (or middleware service name) to associate with route.
145+
* @param string|array|callable|MiddlewareInterface|RequestHandlerInterface $middleware
146+
* Middleware or request handler (or service name resolving to one of
147+
* those types) to associate with route.
144148
* @param null|string $name The name of the route.
145149
*/
146150
public function post(string $path, $middleware, $name = null) : Router\Route
@@ -149,8 +153,9 @@ public function post(string $path, $middleware, $name = null) : Router\Route
149153
}
150154

151155
/**
152-
* @param string|array|callable|MiddlewareInterface $middleware Middleware
153-
* (or middleware service name) to associate with route.
156+
* @param string|array|callable|MiddlewareInterface|RequestHandlerInterface $middleware
157+
* Middleware or request handler (or service name resolving to one of
158+
* those types) to associate with route.
154159
* @param null|string $name The name of the route.
155160
*/
156161
public function put(string $path, $middleware, string $name = null) : Router\Route
@@ -159,8 +164,9 @@ public function put(string $path, $middleware, string $name = null) : Router\Rou
159164
}
160165

161166
/**
162-
* @param string|array|callable|MiddlewareInterface $middleware Middleware
163-
* (or middleware service name) to associate with route.
167+
* @param string|array|callable|MiddlewareInterface|RequestHandlerInterface $middleware
168+
* Middleware or request handler (or service name resolving to one of
169+
* those types) to associate with route.
164170
* @param null|string $name The name of the route.
165171
*/
166172
public function patch(string $path, $middleware, string $name = null) : Router\Route
@@ -169,8 +175,9 @@ public function patch(string $path, $middleware, string $name = null) : Router\R
169175
}
170176

171177
/**
172-
* @param string|array|callable|MiddlewareInterface $middleware Middleware
173-
* (or middleware service name) to associate with route.
178+
* @param string|array|callable|MiddlewareInterface|RequestHandlerInterface $middleware
179+
* Middleware or request handler (or service name resolving to one of
180+
* those types) to associate with route.
174181
* @param null|string $name The name of the route.
175182
*/
176183
public function delete(string $path, $middleware, string $name = null) : Router\Route
@@ -179,8 +186,9 @@ public function delete(string $path, $middleware, string $name = null) : Router\
179186
}
180187

181188
/**
182-
* @param string|array|callable|MiddlewareInterface $middleware Middleware
183-
* (or middleware service name) to associate with route.
189+
* @param string|array|callable|MiddlewareInterface|RequestHandlerInterface $middleware
190+
* Middleware or request handler (or service name resolving to one of
191+
* those types) to associate with route.
184192
* @param null|string $name The name of the route.
185193
*/
186194
public function any(string $path, $middleware, string $name = null) : Router\Route

src/Exception/InvalidMiddlewareException.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace Zend\Expressive\Exception;
1111

1212
use Psr\Http\Server\MiddlewareInterface;
13+
use Psr\Http\Server\RequestHandlerInterface;
1314
use RuntimeException;
1415

1516
class InvalidMiddlewareException extends RuntimeException implements ExceptionInterface
@@ -22,9 +23,10 @@ public static function forMiddleware($middleware) : self
2223
{
2324
return new self(sprintf(
2425
'Middleware "%s" is neither a string service name, a PHP callable,'
25-
. ' a %s instance, or an array of such arguments',
26+
. ' a %s instance, a %s instance, or an array of such arguments',
2627
is_object($middleware) ? get_class($middleware) : gettype($middleware),
27-
MiddlewareInterface::class
28+
MiddlewareInterface::class,
29+
RequestHandlerInterface::class
2830
));
2931
}
3032

src/MiddlewareContainer.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
namespace Zend\Expressive;
1111

1212
use Psr\Container\ContainerInterface;
13+
use Psr\Http\Message\ResponseInterface;
14+
use Psr\Http\Message\ServerRequestInterface;
1315
use Psr\Http\Server\MiddlewareInterface;
16+
use Psr\Http\Server\RequestHandlerInterface;
17+
use Zend\Stratigility\Middleware\RequestHandlerMiddleware;
1418

1519
class MiddlewareContainer implements ContainerInterface
1620
{
@@ -55,6 +59,10 @@ public function get($service) : MiddlewareInterface
5559
? $this->container->get($service)
5660
: new $service();
5761

62+
$middleware = $middleware instanceof RequestHandlerInterface
63+
? new RequestHandlerMiddleware($middleware)
64+
: $middleware;
65+
5866
if (! $middleware instanceof MiddlewareInterface) {
5967
throw Exception\InvalidMiddlewareException::forMiddlewareService($service, $middleware);
6068
}

src/MiddlewareFactory.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
use Psr\Container\ContainerInterface;
1313
use Psr\Http\Server\MiddlewareInterface;
14+
use Psr\Http\Server\RequestHandlerInterface;
1415
use Zend\Stratigility\MiddlewarePipe;
1516
use Zend\Stratigility\Middleware\CallableMiddlewareDecorator;
17+
use Zend\Stratigility\Middleware\RequestHandlerMiddleware;
1618

1719
/**
1820
* Marshal middleware for use in the application.
@@ -23,17 +25,20 @@
2325
* If any middleware provided is already a MiddlewareInterface, it can be used
2426
* verbatim or decorated as-is. Other middleware types acceptable are:
2527
*
28+
* - PSR-15 RequestHandlerInterface instances; these will be decorated as
29+
* RequestHandlerMiddleware instances.
2630
* - string service names resolving to middleware
2731
* - arrays of service names and/or MiddlewareInterface instances
2832
* - PHP callables that follow the PSR-15 signature
2933
*
30-
* Additionally, the class provides several decorator/utility methods:
34+
* Additionally, the class provides the following decorator/utility methods:
3135
*
3236
* - callable() will decorate the callable middleware passed to it using
3337
* CallableMiddlewareDecorator.
34-
* - path() will decorate the path and middleware passed to it using
35-
* PathMiddlewareDecorator, after first passing the middleware to the prepare()
36-
* method.
38+
* - handler() will decorate the request handler passed to it using
39+
* RequestHandlerMiddleware.
40+
* - lazy() will decorate the string service name passed to it, along with the
41+
* factory instance, as a LazyLoadingMiddleware instance.
3742
* - pipeline() will create a MiddlewarePipe instance from the array of
3843
* middleware passed to it, after passing each first to prepare().
3944
*/
@@ -50,7 +55,7 @@ public function __construct(MiddlewareContainer $container)
5055
}
5156

5257
/**
53-
* @param string|array|callable|MiddlewareInterface $middleware
58+
* @param string|array|callable|MiddlewareInterface|RequestHandlerInterface $middleware
5459
* @throws Exception\InvalidMiddlewareException if argument is not one of
5560
* the specified types.
5661
*/
@@ -60,6 +65,10 @@ public function prepare($middleware) : MiddlewareInterface
6065
return $middleware;
6166
}
6267

68+
if ($middleware instanceof RequestHandlerInterface) {
69+
return new RequestHandlerMiddleware($middleware);
70+
}
71+
6372
if (is_callable($middleware)) {
6473
return $this->callable($middleware);
6574
}
@@ -83,6 +92,14 @@ public function callable(callable $middleware) : CallableMiddlewareDecorator
8392
return new CallableMiddlewareDecorator($middleware);
8493
}
8594

95+
/**
96+
* Decorate a RequestHandlerInterface as middleware via RequestHandlerMiddleware.
97+
*/
98+
public function handler(RequestHandlerInterface $handler) : RequestHandlerMiddleware
99+
{
100+
return new RequestHandlerMiddleware($handler);
101+
}
102+
86103
/**
87104
* Create lazy loading middleware based on a service name.
88105
*/

test/MiddlewareContainerTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
namespace ZendTest\Expressive;
1111

1212
use PHPUnit\Framework\TestCase;
13+
use Prophecy\Argument;
1314
use Psr\Container\ContainerInterface;
1415
use Psr\Http\Server\MiddlewareInterface;
16+
use Psr\Http\Server\RequestHandlerInterface;
1517
use Zend\Expressive\Exception;
1618
use Zend\Expressive\MiddlewareContainer;
1719
use Zend\Expressive\Router\DispatchMiddleware;
20+
use Zend\Stratigility\Middleware\RequestHandlerMiddleware;
1821

1922
class MiddlewareContainerTest extends TestCase
2023
{
@@ -85,4 +88,18 @@ public function testGetReturnsInstantiatedClass()
8588
$middleware = $this->container->get(DispatchMiddleware::class);
8689
$this->assertInstanceOf(DispatchMiddleware::class, $middleware);
8790
}
91+
92+
public function testGetWillDecorateARequestHandlerAsMiddleware()
93+
{
94+
$handler = $this->prophesize(RequestHandlerInterface::class)->reveal();
95+
96+
$this->originContainer->has('AHandlerNotMiddleware')->willReturn(true);
97+
$this->originContainer->get('AHandlerNotMiddleware')->willReturn($handler);
98+
99+
$middleware = $this->container->get('AHandlerNotMiddleware');
100+
101+
// Test that we get back middleware decorating the handler
102+
$this->assertInstanceOf(RequestHandlerMiddleware::class, $middleware);
103+
$this->assertAttributeSame($handler, 'handler', $middleware);
104+
}
88105
}

test/MiddlewareFactoryTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use PHPUnit\Framework\TestCase;
1313
use Psr\Http\Server\MiddlewareInterface;
14+
use Psr\Http\Server\RequestHandlerInterface;
1415
use ReflectionProperty;
1516
use Zend\Expressive\Exception;
1617
use Zend\Expressive\MiddlewareContainer;
@@ -19,6 +20,7 @@
1920
use Zend\Expressive\Router\DispatchMiddleware;
2021
use Zend\Stratigility\MiddlewarePipe;
2122
use Zend\Stratigility\Middleware\CallableMiddlewareDecorator;
23+
use Zend\Stratigility\Middleware\RequestHandlerMiddleware;
2224

2325
class MiddlewareFactoryTest extends TestCase
2426
{
@@ -194,4 +196,20 @@ public function testPipelineAllowsPipingArraysOfMiddlewareAndCastsThemToInternal
194196
$this->assertCallableMiddleware($callable, $received[0]);
195197
$this->assertSame($middleware, $received[1]);
196198
}
199+
200+
public function testPrepareDecoratesRequestHandlersAsMiddleware()
201+
{
202+
$handler = $this->prophesize(RequestHandlerInterface::class)->reveal();
203+
$middleware = $this->factory->prepare($handler);
204+
$this->assertInstanceOf(RequestHandlerMiddleware::class, $middleware);
205+
$this->assertAttributeSame($handler, 'handler', $middleware);
206+
}
207+
208+
public function testHandlerDecoratesRequestHandlersAsMiddleware()
209+
{
210+
$handler = $this->prophesize(RequestHandlerInterface::class)->reveal();
211+
$middleware = $this->factory->handler($handler);
212+
$this->assertInstanceOf(RequestHandlerMiddleware::class, $middleware);
213+
$this->assertAttributeSame($handler, 'handler', $middleware);
214+
}
197215
}

0 commit comments

Comments
 (0)