Skip to content

Commit c60e68d

Browse files
committed
Added MiddlewareInterface to the request handler so it can be used either as RequestHandler or as middleware. Introduce BC break
1 parent 1fd8b68 commit c60e68d

File tree

3 files changed

+105
-16
lines changed

3 files changed

+105
-16
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ Lightweight & dead simple PSR-15 Server Request Handler implementation to proces
1010

1111
### Description
1212

13-
PSR-7 Request Handler implementing the [RequestHandlerInterface](https://github.com/php-fig/http-server-handler/blob/master/src/RequestHandlerInterface.php)
14-
and able to manage a collection of Middlewares implementing the [MiddlewareInterface](https://github.com/php-fig/http-server-middleware/blob/master/src/MiddlewareInterface.php).
13+
PSR-15 request handler implementing both [RequestHandlerInterface](https://github.com/php-fig/http-server-handler/blob/master/src/RequestHandlerInterface.php) and [MiddlewareInterface](https://github.com/php-fig/http-server-middleware/blob/master/src/MiddlewareInterface.php)
14+
able to manage a collection of middlewares implementing the [MiddlewareInterface](https://github.com/php-fig/http-server-middleware/blob/master/src/MiddlewareInterface.php).
1515

16-
It comes with a set of middleware collections using different strategy on how to provide the middlewares to the RequestHandler, and also provide a dead simple collection interface to implement in a glimpse your own strategy.
16+
It can be used either as a RequestHandler or as a Middleware to fit into any implementation.
17+
18+
Comes with a set of middleware collections using different strategy (LIFO, FIFO...) on how the middlewares from the collection are provided to the RequestHandler, and also provides a simple MiddlewareCollectionInterface to implement in a glimpse your own strategy.
1719

1820
### Goals
1921

src/RequestHandler.php

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@
2626

2727
namespace NoGlitchYo\MiddlewareCollectionRequestHandler;
2828

29+
use LogicException;
2930
use Psr\Http\Message\ResponseInterface;
3031
use Psr\Http\Message\ServerRequestInterface;
32+
use Psr\Http\Server\MiddlewareInterface;
3133
use Psr\Http\Server\RequestHandlerInterface;
3234

33-
class RequestHandler implements RequestHandlerInterface
35+
class RequestHandler implements RequestHandlerInterface, MiddlewareInterface
3436
{
3537
use RequestHandlerTrait;
3638

@@ -40,15 +42,15 @@ class RequestHandler implements RequestHandlerInterface
4042
private $middlewareCollection;
4143

4244
/**
43-
* @var RequestHandlerInterface
45+
* @var RequestHandlerInterface|null
4446
*/
4547
private $defaultRequestHandler;
4648

4749
public function __construct(
48-
RequestHandlerInterface $defaultRequestHandler,
49-
MiddlewareCollectionInterface $middlewareCollection
50+
MiddlewareCollectionInterface $middlewareCollection,
51+
RequestHandlerInterface $defaultRequestHandler = null
5052
) {
51-
$this->middlewareCollection = $middlewareCollection;
53+
$this->middlewareCollection = $middlewareCollection;
5254
$this->defaultRequestHandler = $defaultRequestHandler;
5355
}
5456

@@ -57,7 +59,7 @@ public static function fromCallable(
5759
MiddlewareCollectionInterface $middlewareCollection
5860
): self {
5961
$defaultRequestHandler = static::createRequestHandlerFromCallable($callable);
60-
return new static($defaultRequestHandler, $middlewareCollection);
62+
return new static($middlewareCollection, $defaultRequestHandler);
6163
}
6264

6365
public function __invoke(ServerRequestInterface $serverRequest): ResponseInterface
@@ -67,6 +69,12 @@ public function __invoke(ServerRequestInterface $serverRequest): ResponseInterfa
6769

6870
public function handle(ServerRequestInterface $request): ResponseInterface
6971
{
72+
if ($this->defaultRequestHandler === null) {
73+
throw new LogicException(
74+
'A default request handler must be defined if RequestHandler is used as a RequestHandler.'
75+
);
76+
}
77+
7078
if ($this->middlewareCollection->isEmpty()) {
7179
return $this->defaultRequestHandler->handle($request);
7280
}
@@ -75,4 +83,15 @@ public function handle(ServerRequestInterface $request): ResponseInterface
7583

7684
return $nextMiddleware->process($request, $this);
7785
}
86+
87+
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
88+
{
89+
if ($this->middlewareCollection->isEmpty()) {
90+
return $handler->handle($request);
91+
}
92+
93+
$nextMiddleware = $this->middlewareCollection->next();
94+
95+
return $nextMiddleware->process($request, $this);
96+
}
7897
}

tests/RequestHandlerTest.php

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
namespace NoGlitchYo\MiddlewareCollectionRequestHandler\Tests;
2727

28+
use LogicException;
2829
use NoGlitchYo\MiddlewareCollectionRequestHandler\MiddlewareCollectionInterface;
2930
use NoGlitchYo\MiddlewareCollectionRequestHandler\RequestHandler;
3031
use Nyholm\Psr7\Response;
@@ -59,15 +60,15 @@ class RequestHandlerTest extends TestCase
5960

6061
protected function setUp(): void
6162
{
62-
$this->requestHandlerMock = $this->createMock(RequestHandlerInterface::class);
63+
$this->requestHandlerMock = $this->createMock(RequestHandlerInterface::class);
6364
$this->middlewareCollectionMock = $this->createMock(MiddlewareCollectionInterface::class);
6465

65-
$this->sut = new RequestHandler($this->requestHandlerMock, $this->middlewareCollectionMock);
66+
$this->sut = new RequestHandler($this->middlewareCollectionMock, $this->requestHandlerMock);
6667
}
6768

6869
public function testHandleDelegateToDefaultHandlerIfNoMiddleware()
6970
{
70-
$request = new ServerRequest('GET', '/test');
71+
$request = new ServerRequest('GET', '/test');
7172
$response = new Response(404);
7273

7374
$this->middlewareCollectionMock->method('isEmpty')
@@ -87,7 +88,7 @@ function (ServerRequestInterface $request) use ($response) {
8788

8889
public function testIsCallable()
8990
{
90-
$request = new ServerRequest('GET', '/test');
91+
$request = new ServerRequest('GET', '/test');
9192
$response = new Response(404);
9293

9394
$this->middlewareCollectionMock->method('isEmpty')
@@ -136,15 +137,29 @@ public function testHandleCallNextMiddlewareWithInstanceOfThisAsHandler()
136137
->method('next')
137138
->willReturnOnConsecutiveCalls(
138139
self::getMiddleware(false),
139-
self::getMiddleware(true),
140-
);
140+
self::getMiddleware(true)
141+
);
141142

142143
$this->assertInstanceOf(ResponseInterface::class, $this->sut->handle($request));
143144
}
144145

145-
public function testFromCallableCreateDefaultRequestHandlerFromCallable()
146+
public function testHandleThrowExceptionIfDefaultRequestHandlerIsNull()
146147
{
147148
$request = new ServerRequest('GET', '/test');
149+
150+
$this->middlewareCollectionMock = $this->createMock(MiddlewareCollectionInterface::class);
151+
152+
$sut = new RequestHandler($this->middlewareCollectionMock);
153+
154+
$this->expectException(LogicException::class);
155+
$this->expectExceptionMessage('A default request handler must be defined if RequestHandler is used as a RequestHandler.');
156+
157+
$sut->handle($request);
158+
}
159+
160+
public function testFromCallableCreateDefaultRequestHandlerFromCallable()
161+
{
162+
$request = new ServerRequest('GET', '/test');
148163
$response = new Response(405);
149164
$callable = function () use ($response) {
150165
return $response;
@@ -158,5 +173,58 @@ public function testFromCallableCreateDefaultRequestHandlerFromCallable()
158173
$this->assertSame($response, $sut->handle($request));
159174
}
160175

176+
public function testProcessCallNextMiddleware()
177+
{
178+
$request = new ServerRequest('GET', '/test');
179+
180+
$this->middlewareCollectionMock
181+
->expects($this->once())
182+
->method('isEmpty')
183+
->willReturn(false);
161184

185+
$this->middlewareCollectionMock
186+
->expects($this->once())
187+
->method('next')
188+
->willReturn(self::getMiddleware(true));
189+
190+
$handler = new class implements RequestHandlerInterface
191+
{
192+
public function handle(ServerRequestInterface $request): ResponseInterface
193+
{
194+
return new Response();
195+
}
196+
};
197+
198+
$this->assertInstanceOf(ResponseInterface::class, $this->sut->process($request, $handler));
199+
}
200+
201+
202+
public function testProcessReturnAndDelegateToHandlerIfNoMiddleware()
203+
{
204+
$request = new ServerRequest('GET', '/test');
205+
$response = new Response(404);
206+
207+
$this->middlewareCollectionMock->method('isEmpty')
208+
->willReturn(true);
209+
210+
$handler = new class($response) implements RequestHandlerInterface
211+
{
212+
/**
213+
* @var ResponseInterface
214+
*/
215+
private $response;
216+
217+
public function __construct(ResponseInterface $response)
218+
{
219+
$this->response = $response;
220+
}
221+
222+
public function handle(ServerRequestInterface $request): ResponseInterface
223+
{
224+
return $this->response;
225+
}
226+
};
227+
228+
$this->assertSame($response, $this->sut->process($request, $handler));
229+
}
162230
}

0 commit comments

Comments
 (0)