Skip to content

Commit 7c79c3b

Browse files
authored
Cleanup middleware dispatchers (#337)
1 parent 410dc36 commit 7c79c3b

6 files changed

Lines changed: 50 additions & 299 deletions

File tree

src/Middleware/Consume/ConsumeMiddlewareDispatcher.php

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ final class ConsumeMiddlewareDispatcher
2020
/**
2121
* @var array[]|callable[]|ConsumeMiddlewareInterface[]|string[]
2222
*/
23-
private array $middlewareDefinitions;
23+
private readonly array $middlewareDefinitions;
2424

2525
public function __construct(
2626
private readonly ConsumeMiddlewareFactoryInterface $middlewareFactory,
@@ -47,43 +47,6 @@ public function dispatch(
4747
return $this->stack[$type]->handleConsume($request);
4848
}
4949

50-
/**
51-
* Returns new instance with middleware handlers replaced with the ones provided.
52-
* The last specified handler will be executed first.
53-
*
54-
* @param array[]|callable[]|ConsumeMiddlewareInterface[]|string[] $middlewareDefinitions Each array element is:
55-
*
56-
* - A name of a middleware class. The middleware instance will be obtained from container executed.
57-
* - A callable with `function(ServerRequestInterface $request, RequestHandlerInterface $handler):
58-
* ResponseInterface` signature.
59-
* - A "callable-like" array in format `[FooMiddleware::class, 'index']`. `FooMiddleware` instance will
60-
* be created and `index()` method will be executed.
61-
* - A function returning middleware. The middleware returned will be executed.
62-
*
63-
* For callables typed parameters are automatically injected using dependency injection container.
64-
*
65-
* @return self New instance of the {@see ConsumeMiddlewareDispatcher}
66-
*/
67-
public function withMiddlewares(array $middlewareDefinitions): self
68-
{
69-
$instance = clone $this;
70-
$instance->middlewareDefinitions = array_reverse($middlewareDefinitions);
71-
72-
// Fixes a memory leak.
73-
unset($instance->stack);
74-
$instance->stack = [];
75-
76-
return $instance;
77-
}
78-
79-
/**
80-
* @return bool Whether there are middleware defined in the dispatcher.
81-
*/
82-
public function hasMiddlewares(): bool
83-
{
84-
return $this->middlewareDefinitions !== [];
85-
}
86-
8750
/**
8851
* @psalm-return list<Closure():ConsumeMiddlewareInterface>
8952
*/

src/Middleware/FailureHandling/FailureMiddlewareDispatcher.php

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,20 @@ final class FailureMiddlewareDispatcher
1717
*/
1818
private array $stack = [];
1919

20+
/**
21+
* @var array[][]|callable[][]|FailureMiddlewareInterface[][]|string[][]
22+
*/
23+
private readonly array $middlewareDefinitions;
24+
2025
/**
2126
* @param array[][]|callable[][]|FailureMiddlewareInterface[][]|string[][] $middlewareDefinitions
2227
*/
2328
public function __construct(
2429
private readonly FailureMiddlewareFactoryInterface $middlewareFactory,
25-
private array $middlewareDefinitions,
30+
array $middlewareDefinitions,
2631
) {
27-
$this->init();
32+
$middlewareDefinitions[self::DEFAULT_PIPELINE] ??= [];
33+
$this->middlewareDefinitions = $middlewareDefinitions;
2834
}
2935

3036
/**
@@ -48,42 +54,6 @@ public function dispatch(
4854
return $this->stack[$queueName]->handleFailure($request);
4955
}
5056

51-
/**
52-
* Returns new instance with middleware handlers replaced with the ones provided.
53-
* The last specified handler will be executed first.
54-
*
55-
* @param array[][]|callable[][]|FailureMiddlewareInterface[][]|string[][] $middlewareDefinitions Each array element is:
56-
*
57-
* - A name of a middleware class. The middleware instance will be obtained from container executed.
58-
* - A callable with `function(ServerRequestInterface $request, RequestHandlerInterface $handler):
59-
* ResponseInterface` signature.
60-
* - A "callable-like" array in format `[FooMiddleware::class, 'index']`. `FooMiddleware` instance will
61-
* be created and `index()` method will be executed.
62-
* - A function returning a middleware. The middleware returned will be executed.
63-
*
64-
* For callables typed parameters are automatically injected using dependency injection container.
65-
*
66-
* @return self New instance of the {@see FailureMiddlewareDispatcher}
67-
*/
68-
public function withMiddlewares(array $middlewareDefinitions): self
69-
{
70-
$instance = clone $this;
71-
$instance->middlewareDefinitions = $middlewareDefinitions;
72-
73-
// Fixes a memory leak.
74-
unset($instance->stack);
75-
$instance->stack = [];
76-
77-
$instance->init();
78-
79-
return $instance;
80-
}
81-
82-
private function init(): void
83-
{
84-
$this->middlewareDefinitions[self::DEFAULT_PIPELINE] ??= [];
85-
}
86-
8757
/**
8858
* @psalm-return list<Closure():FailureMiddlewareInterface>
8959
*/

src/Middleware/Push/PushMiddlewareDispatcher.php

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ final class PushMiddlewareDispatcher
2626
*/
2727
public function __construct(
2828
private readonly PushMiddlewareFactoryInterface $middlewareFactory,
29-
private array $middlewareDefinitions,
30-
private PushHandlerInterface $finalHandler,
29+
private readonly array $middlewareDefinitions,
30+
private readonly PushHandlerInterface $finalHandler,
3131
) {}
3232

3333
/**
@@ -42,57 +42,6 @@ public function dispatch(MessageInterface $message): MessageInterface
4242
return $this->stack->handlePush($message);
4343
}
4444

45-
public function withFinalHandler(PushHandlerInterface $finalHandler): self
46-
{
47-
$instance = clone $this;
48-
$instance->finalHandler = $finalHandler;
49-
50-
// Fixes a memory leak.
51-
unset($instance->stack);
52-
$instance->stack = null;
53-
54-
return $instance;
55-
}
56-
57-
/**
58-
* Returns new instance with middleware handlers replaced with the ones provided.
59-
*
60-
* @param mixed[] $middlewareDefinitions Middleware definitions.
61-
*
62-
* @return self New instance of the {@see PushMiddlewareDispatcher}
63-
*/
64-
public function withMiddlewares(array $middlewareDefinitions): self
65-
{
66-
$instance = clone $this;
67-
$instance->middlewareDefinitions = $middlewareDefinitions;
68-
69-
// Fixes a memory leak.
70-
unset($instance->stack);
71-
$instance->stack = null;
72-
73-
return $instance;
74-
}
75-
76-
/**
77-
* Returns a new instance with additional middleware handlers added to the existing ones.
78-
*
79-
* @param mixed[] $middlewareDefinitions Middleware definitions.
80-
*
81-
* @return self New instance of the {@see PushMiddlewareDispatcher}
82-
*/
83-
public function withMiddlewaresAdded(array $middlewareDefinitions): self
84-
{
85-
return $this->withMiddlewares([...$this->middlewareDefinitions, ...$middlewareDefinitions]);
86-
}
87-
88-
/**
89-
* @return bool Whether there are middleware defined in the dispatcher.
90-
*/
91-
public function hasMiddlewares(): bool
92-
{
93-
return $this->middlewareDefinitions !== [];
94-
}
95-
9645
/**
9746
* @psalm-return list<Closure():PushMiddlewareInterface>
9847
*/

tests/Unit/Middleware/Consume/MiddlewareDispatcherTest.php

Lines changed: 11 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Yiisoft\Queue\Tests\Unit\Middleware\Consume;
66

7-
use PHPUnit\Framework\Attributes\DataProvider;
87
use PHPUnit\Framework\TestCase;
98
use Psr\Container\ContainerInterface;
109
use Yiisoft\Test\Support\Container\SimpleContainer;
@@ -24,13 +23,11 @@ public function testCallableMiddlewareCalled(): void
2423
{
2524
$request = $this->getConsumeRequest();
2625

27-
$dispatcher = $this->createDispatcher()->withMiddlewares(
28-
[
29-
static function (ConsumeRequest $request): ConsumeRequest {
30-
return $request->withMessage(new GenericMessage('test', 'New closure test data'))->withQueueName('other-queue');
31-
},
32-
],
33-
);
26+
$dispatcher = $this->createDispatcher(middlewareDefinitions: [
27+
static function (ConsumeRequest $request): ConsumeRequest {
28+
return $request->withMessage(new GenericMessage('test', 'New closure test data'))->withQueueName('other-queue');
29+
},
30+
]);
3431

3532
$request = $dispatcher->dispatch($request, $this->getRequestHandler());
3633
$this->assertSame('New closure test data', $request->getMessage()->getPayload());
@@ -44,7 +41,7 @@ public function testArrayMiddlewareCallableDefinition(): void
4441
TestCallableMiddleware::class => new TestCallableMiddleware(),
4542
],
4643
);
47-
$dispatcher = $this->createDispatcher($container)->withMiddlewares([[TestCallableMiddleware::class, 'index']]);
44+
$dispatcher = $this->createDispatcher($container, [[TestCallableMiddleware::class, 'index']]);
4845
$request = $dispatcher->dispatch($request, $this->getRequestHandler());
4946
$this->assertSame('New test data', $request->getMessage()->getPayload());
5047
}
@@ -57,7 +54,7 @@ public function testFactoryArrayDefinition(): void
5754
'class' => TestMiddleware::class,
5855
'__construct()' => ['message' => 'New test data from the definition'],
5956
];
60-
$dispatcher = $this->createDispatcher($container)->withMiddlewares([$definition]);
57+
$dispatcher = $this->createDispatcher($container, [$definition]);
6158
$request = $dispatcher->dispatch($request, $this->getRequestHandler());
6259
$this->assertSame('New test data from the definition', $request->getMessage()->getPayload());
6360
}
@@ -77,7 +74,7 @@ public function testMiddlewareFullStackCalled(): void
7774
return $handler->handleConsume($request);
7875
};
7976

80-
$dispatcher = $this->createDispatcher()->withMiddlewares([$middleware1, $middleware2]);
77+
$dispatcher = $this->createDispatcher(middlewareDefinitions: [$middleware1, $middleware2]);
8178

8279
$request = $dispatcher->dispatch($request, $this->getRequestHandler());
8380
$this->assertSame('new test data', $request->getMessage()->getPayload());
@@ -95,56 +92,12 @@ public function testMiddlewareStackInterrupted(): void
9592
return $request->withMessage(new GenericMessage($request->getMessage()->getType(), 'second'));
9693
};
9794

98-
$dispatcher = $this->createDispatcher()->withMiddlewares([$middleware1, $middleware2]);
95+
$dispatcher = $this->createDispatcher(middlewareDefinitions: [$middleware1, $middleware2]);
9996

10097
$request = $dispatcher->dispatch($request, $this->getRequestHandler());
10198
$this->assertSame('first', $request->getMessage()->getPayload());
10299
}
103100

104-
public static function dataHasMiddlewares(): array
105-
{
106-
return [
107-
[[], false],
108-
[[[TestCallableMiddleware::class, 'index']], true],
109-
];
110-
}
111-
112-
#[DataProvider('dataHasMiddlewares')]
113-
public function testHasMiddlewares(array $definitions, bool $expected): void
114-
{
115-
self::assertSame(
116-
$expected,
117-
$this->createDispatcher()->withMiddlewares($definitions)->hasMiddlewares(),
118-
);
119-
}
120-
121-
public function testImmutability(): void
122-
{
123-
$dispatcher = $this->createDispatcher();
124-
self::assertNotSame($dispatcher, $dispatcher->withMiddlewares([]));
125-
}
126-
127-
public function testResetStackOnWithMiddlewares(): void
128-
{
129-
$request = $this->getConsumeRequest();
130-
$container = $this->createContainer(
131-
[
132-
TestCallableMiddleware::class => new TestCallableMiddleware(),
133-
TestMiddleware::class => new TestMiddleware(),
134-
],
135-
);
136-
137-
$dispatcher = $this
138-
->createDispatcher($container)
139-
->withMiddlewares([[TestCallableMiddleware::class, 'index']]);
140-
$dispatcher->dispatch($request, $this->getRequestHandler());
141-
142-
$dispatcher = $dispatcher->withMiddlewares([TestMiddleware::class]);
143-
$request = $dispatcher->dispatch($request, $this->getRequestHandler());
144-
145-
self::assertSame('New middleware test data', $request->getMessage()->getPayload());
146-
}
147-
148101
private function getRequestHandler(): ConsumeHandlerInterface
149102
{
150103
return new class implements ConsumeHandlerInterface {
@@ -157,12 +110,11 @@ public function handleConsume(ConsumeRequest $request): ConsumeRequest
157110

158111
private function createDispatcher(
159112
?ContainerInterface $container = null,
113+
array $middlewareDefinitions = [],
160114
): ConsumeMiddlewareDispatcher {
161115
$container ??= $this->createContainer([AdapterInterface::class => new InMemoryAdapter()]);
162116

163-
return new ConsumeMiddlewareDispatcher(
164-
new ConsumeMiddlewareFactory($container),
165-
);
117+
return new ConsumeMiddlewareDispatcher(new ConsumeMiddlewareFactory($container), ...$middlewareDefinitions);
166118
}
167119

168120
private function createContainer(array $instances = []): ContainerInterface

0 commit comments

Comments
 (0)