Skip to content

Commit b5de434

Browse files
samdarkyiiliveext
andauthored
Add events
Co-authored-by: Alexander Makarov <[email protected]> Co-authored-by: yiiliveext <[email protected]>
1 parent 25bc94d commit b5de434

7 files changed

+177
-12
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
[![static analysis](https://github.com/yiisoft/middleware-dispatcher/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/middleware-dispatcher/actions?query=workflow%3A%22static+analysis%22)
1616
[![type-coverage](https://shepherd.dev/github/yiisoft/middleware-dispatcher/coverage.svg)](https://shepherd.dev/github/yiisoft/middleware-dispatcher)
1717

18-
The package ...
18+
The package is a [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware dispatcher.
1919

2020
## Installation
2121

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"psr/http-message": "^1.0",
2323
"psr/http-server-handler": "^1.0",
2424
"psr/http-server-middleware": "^1.0",
25-
"yiisoft/injector": "^1.0"
25+
"yiisoft/injector": "^1.0",
26+
"psr/event-dispatcher": "^1.0"
2627
},
2728
"require-dev": {
2829
"nyholm/psr7": "^1.3",

src/Event/AfterMiddleware.php

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Middleware\Dispatcher\Event;
6+
7+
use Psr\Http\Message\ResponseInterface;
8+
use Psr\Http\Server\MiddlewareInterface;
9+
10+
/**
11+
* AfterMiddleware event is raised after a middleware was executed
12+
*/
13+
final class AfterMiddleware
14+
{
15+
private MiddlewareInterface $middleware;
16+
private ?ResponseInterface $response;
17+
18+
public function __construct(MiddlewareInterface $middleware, ?ResponseInterface $response)
19+
{
20+
$this->middleware = $middleware;
21+
$this->response = $response;
22+
}
23+
24+
/**
25+
* @return MiddlewareInterface middleware that was executed
26+
*/
27+
public function getMiddleware(): MiddlewareInterface
28+
{
29+
return $this->middleware;
30+
}
31+
32+
/**
33+
* @return ResponseInterface|null response generated by middleware or null in case there was an error
34+
*/
35+
public function getResponse(): ?ResponseInterface
36+
{
37+
return $this->response;
38+
}
39+
}

src/Event/BeforeMiddleware.php

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Middleware\Dispatcher\Event;
6+
7+
use Psr\Http\Message\ServerRequestInterface;
8+
use Psr\Http\Server\MiddlewareInterface;
9+
10+
final class BeforeMiddleware
11+
{
12+
private MiddlewareInterface $middleware;
13+
private ServerRequestInterface $request;
14+
15+
public function __construct(MiddlewareInterface $middleware, ServerRequestInterface $request)
16+
{
17+
$this->middleware = $middleware;
18+
$this->request = $request;
19+
}
20+
21+
public function getMiddleware(): MiddlewareInterface
22+
{
23+
return $this->middleware;
24+
}
25+
26+
public function getRequest(): ServerRequestInterface
27+
{
28+
return $this->request;
29+
}
30+
}

src/MiddlewareStack.php

+22-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
namespace Yiisoft\Middleware\Dispatcher;
66

7+
use Psr\EventDispatcher\EventDispatcherInterface;
78
use Psr\Http\Message\ResponseInterface;
89
use Psr\Http\Message\ServerRequestInterface;
910
use Psr\Http\Server\MiddlewareInterface;
1011
use Psr\Http\Server\RequestHandlerInterface;
12+
use Yiisoft\Middleware\Dispatcher\Event\AfterMiddleware;
13+
use Yiisoft\Middleware\Dispatcher\Event\BeforeMiddleware;
1114

1215
final class MiddlewareStack implements MiddlewareStackInterface
1316
{
@@ -19,6 +22,13 @@ final class MiddlewareStack implements MiddlewareStackInterface
1922
*/
2023
private ?RequestHandlerInterface $stack = null;
2124

25+
private EventDispatcherInterface $eventDispatcher;
26+
27+
public function __construct(EventDispatcherInterface $eventDispatcher)
28+
{
29+
$this->eventDispatcher = $eventDispatcher;
30+
}
31+
2232
public function build(array $middlewares, RequestHandlerInterface $fallbackHandler): MiddlewareStackInterface
2333
{
2434
$handler = $fallbackHandler;
@@ -56,19 +66,28 @@ public function isEmpty(): bool
5666
*/
5767
private function wrap(MiddlewareInterface $middleware, RequestHandlerInterface $handler): RequestHandlerInterface
5868
{
59-
return new class($middleware, $handler) implements RequestHandlerInterface {
69+
return new class($middleware, $handler, $this->eventDispatcher) implements RequestHandlerInterface {
6070
private MiddlewareInterface $middleware;
6171
private RequestHandlerInterface $handler;
72+
private EventDispatcherInterface $eventDispatcher;
6273

63-
public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $handler)
74+
public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $handler, EventDispatcherInterface $eventDispatcher)
6475
{
6576
$this->middleware = $middleware;
6677
$this->handler = $handler;
78+
$this->eventDispatcher = $eventDispatcher;
6779
}
6880

6981
public function handle(ServerRequestInterface $request): ResponseInterface
7082
{
71-
return $this->middleware->process($request, $this->handler);
83+
$this->eventDispatcher->dispatch(new BeforeMiddleware($this->middleware, $request));
84+
85+
$response = null;
86+
try {
87+
return $response = $this->middleware->process($request, $this->handler);
88+
} finally {
89+
$this->eventDispatcher->dispatch(new AfterMiddleware($this->middleware, $response));
90+
}
7291
}
7392
};
7493
}

tests/MiddlewareDispatcherTest.php

+48-7
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@
88
use Nyholm\Psr7\ServerRequest;
99
use PHPUnit\Framework\TestCase;
1010
use Psr\Container\ContainerInterface;
11+
use Psr\EventDispatcher\EventDispatcherInterface;
1112
use Psr\Http\Message\ResponseInterface;
1213
use Psr\Http\Message\ServerRequestInterface;
1314
use Psr\Http\Server\RequestHandlerInterface;
15+
use Yiisoft\Middleware\Dispatcher\Event\AfterMiddleware;
16+
use Yiisoft\Middleware\Dispatcher\Event\BeforeMiddleware;
1417
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;
1518
use Yiisoft\Middleware\Dispatcher\MiddlewareFactory;
1619
use Yiisoft\Middleware\Dispatcher\MiddlewareStack;
1720
use Yiisoft\Middleware\Dispatcher\Tests\Support\Container;
21+
use Yiisoft\Middleware\Dispatcher\Tests\Support\MockEventDispatcher;
1822
use Yiisoft\Middleware\Dispatcher\Tests\Support\TestController;
1923

2024
final class MiddlewareDispatcherTest extends TestCase
@@ -62,11 +66,11 @@ public function testMiddlewareFullStackCalled(): void
6266
{
6367
$request = new ServerRequest('GET', '/');
6468

65-
$middleware1 = function (ServerRequestInterface $request, RequestHandlerInterface $handler) {
69+
$middleware1 = static function (ServerRequestInterface $request, RequestHandlerInterface $handler) {
6670
$request = $request->withAttribute('middleware', 'middleware1');
6771
return $handler->handle($request);
6872
};
69-
$middleware2 = function (ServerRequestInterface $request) {
73+
$middleware2 = static function (ServerRequestInterface $request) {
7074
return new Response(200, [], null, '1.1', implode($request->getAttributes()));
7175
};
7276

@@ -81,10 +85,10 @@ public function testMiddlewareStackInterrupted(): void
8185
{
8286
$request = new ServerRequest('GET', '/');
8387

84-
$middleware1 = function () {
88+
$middleware1 = static function () {
8589
return new Response(403);
8690
};
87-
$middleware2 = function () {
91+
$middleware2 = static function () {
8892
return new Response(200);
8993
};
9094

@@ -106,6 +110,33 @@ public function testArrayMiddlewareSuccessfulCall(): void
106110
$this->assertSame(200, $response->getStatusCode());
107111
}
108112

113+
public function testEventsAreDispatched(): void
114+
{
115+
$eventDispatcher = new MockEventDispatcher();
116+
117+
$request = new ServerRequest('GET', '/');
118+
119+
$middleware1 = static function (ServerRequestInterface $request, RequestHandlerInterface $handler) {
120+
return $handler->handle($request);
121+
};
122+
$middleware2 = static function () {
123+
return new Response();
124+
};
125+
126+
$dispatcher = $this->getDispatcher(null, $eventDispatcher)->withMiddlewares([$middleware2, $middleware1]);
127+
$dispatcher->dispatch($request, $this->getRequestHandler());
128+
129+
$this->assertEquals(
130+
[
131+
BeforeMiddleware::class,
132+
BeforeMiddleware::class,
133+
AfterMiddleware::class,
134+
AfterMiddleware::class,
135+
],
136+
$eventDispatcher->getClassesEvents()
137+
);
138+
}
139+
109140
private function getRequestHandler(): RequestHandlerInterface
110141
{
111142
return new class() implements RequestHandlerInterface {
@@ -116,13 +147,23 @@ public function handle(ServerRequestInterface $request): ResponseInterface
116147
};
117148
}
118149

119-
private function getDispatcher(ContainerInterface $container = null): MiddlewareDispatcher
150+
private function getDispatcher(ContainerInterface $container = null, ?EventDispatcherInterface $eventDispatcher = null): MiddlewareDispatcher
120151
{
152+
if ($eventDispatcher === null) {
153+
$eventDispatcher = $this->createMock(EventDispatcherInterface::class);
154+
}
155+
121156
if ($container === null) {
122-
return new MiddlewareDispatcher(new MiddlewareFactory($this->getContainer()), new MiddlewareStack());
157+
return new MiddlewareDispatcher(
158+
new MiddlewareFactory($this->getContainer()),
159+
new MiddlewareStack($eventDispatcher)
160+
);
123161
}
124162

125-
return new MiddlewareDispatcher(new MiddlewareFactory($container), new MiddlewareStack());
163+
return new MiddlewareDispatcher(
164+
new MiddlewareFactory($container),
165+
new MiddlewareStack($eventDispatcher)
166+
);
126167
}
127168

128169
private function getContainer(array $instances = []): ContainerInterface

tests/Support/MockEventDispatcher.php

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Middleware\Dispatcher\Tests\Support;
6+
7+
use Psr\EventDispatcher\EventDispatcherInterface;
8+
9+
class MockEventDispatcher implements EventDispatcherInterface
10+
{
11+
private array $events = [];
12+
13+
public function dispatch(object $event): void
14+
{
15+
$this->events[] = $event;
16+
}
17+
18+
public function getClassesEvents(): array
19+
{
20+
return array_map(
21+
static fn ($event) => get_class($event),
22+
$this->events
23+
);
24+
}
25+
26+
public function getEvents(): array
27+
{
28+
return $this->events;
29+
}
30+
31+
public function getFirstEvent(): ?object
32+
{
33+
return array_shift($this->events);
34+
}
35+
}

0 commit comments

Comments
 (0)