Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b38a927

Browse filesBrowse files
[Messenger] ease testing and allow forking the middleware stack
1 parent 7e56ef1 commit b38a927
Copy full SHA for b38a927

File tree

4 files changed

+122
-28
lines changed
Filter options

4 files changed

+122
-28
lines changed

‎src/Symfony/Component/Messenger/Middleware/StackInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Messenger/Middleware/StackInterface.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
/**
1515
* @author Nicolas Grekas <p@tchwork.com>
1616
*
17+
* Implementations must be cloneable, and each clone must unstack the stack independently.
18+
*
1719
* @experimental in 4.2
1820
*/
1921
interface StackInterface

‎src/Symfony/Component/Messenger/Middleware/StackMiddleware.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Messenger/Middleware/StackMiddleware.php
+54-11Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,74 @@
2020
*/
2121
class StackMiddleware implements MiddlewareInterface, StackInterface
2222
{
23-
private $middlewareIterator;
23+
private $stack;
24+
private $offset = 0;
2425

25-
public function __construct(\Iterator $middlewareIterator = null)
26+
/**
27+
* @param iterable|MiddlewareInterface[]|MiddlewareInterface|null $middlewareIterator
28+
*/
29+
public function __construct($middlewareIterator = null)
2630
{
27-
$this->middlewareIterator = $middlewareIterator;
31+
$this->stack = new MiddlewareStack();
32+
33+
if (null === $middlewareIterator) {
34+
return;
35+
}
36+
37+
if ($middlewareIterator instanceof \Iterator) {
38+
$this->stack->iterator = $middlewareIterator;
39+
} elseif ($middlewareIterator instanceof MiddlewareInterface) {
40+
$this->stack->stack[] = $middlewareIterator;
41+
} elseif (!\is_iterable($middlewareIterator)) {
42+
throw new \TypeError(sprintf('Argument 1 passed to %s() must be iterable of %s, %s given.', __METHOD__, MiddlewareInterface::class, \is_object($middlewareIterator) ? \get_class($middlewareIterator) : \gettype($middlewareIterator)));
43+
} else {
44+
$this->stack->iterator = (function () use ($middlewareIterator) {
45+
yield from $middlewareIterator;
46+
})();
47+
}
2848
}
2949

3050
public function next(): MiddlewareInterface
3151
{
32-
if (null === $iterator = $this->middlewareIterator) {
52+
if (null === $next = $this->stack->next($this->offset)) {
3353
return $this;
3454
}
35-
$iterator->next();
3655

37-
if (!$iterator->valid()) {
38-
$this->middlewareIterator = null;
56+
++$this->offset;
3957

40-
return $this;
41-
}
42-
43-
return $iterator->current();
58+
return $next;
4459
}
4560

4661
public function handle(Envelope $envelope, StackInterface $stack): Envelope
4762
{
4863
return $envelope;
4964
}
5065
}
66+
67+
/**
68+
* @internal
69+
*/
70+
class MiddlewareStack
71+
{
72+
public $iterator;
73+
public $stack = [];
74+
75+
public function next(int $offset): ?MiddlewareInterface
76+
{
77+
if (isset($this->stack[$offset])) {
78+
return $this->stack[$offset];
79+
}
80+
81+
if (null === $this->iterator) {
82+
return null;
83+
}
84+
85+
$this->iterator->next();
86+
87+
if (!$this->iterator->valid()) {
88+
return $this->iterator = null;
89+
}
90+
91+
return $this->stack[] = $this->iterator->current();
92+
}
93+
}

‎src/Symfony/Component/Messenger/Test/Middleware/MiddlewareTestCase.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Messenger/Test/Middleware/MiddlewareTestCase.php
+14-17Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Messenger\Envelope;
1616
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
1717
use Symfony\Component\Messenger\Middleware\StackInterface;
18+
use Symfony\Component\Messenger\Middleware\StackMiddleware;
1819

1920
/**
2021
* @author Nicolas Grekas <p@tchwork.com>
@@ -25,23 +26,26 @@ abstract class MiddlewareTestCase extends TestCase
2526
{
2627
protected function getStackMock(bool $nextIsCalled = true)
2728
{
29+
if (!$nextIsCalled) {
30+
$stack = $this->createMock(StackInterface::class);
31+
$stack
32+
->expects($this->never())
33+
->method('next')
34+
;
35+
36+
return $stack;
37+
}
38+
2839
$nextMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
2940
$nextMiddleware
30-
->expects($nextIsCalled ? $this->once() : $this->never())
41+
->expects($this->once())
3142
->method('handle')
3243
->willReturnCallback(function (Envelope $envelope, StackInterface $stack): Envelope {
3344
return $envelope;
3445
})
3546
;
3647

37-
$stack = $this->createMock(StackInterface::class);
38-
$stack
39-
->expects($nextIsCalled ? $this->once() : $this->never())
40-
->method('next')
41-
->willReturn($nextMiddleware)
42-
;
43-
44-
return $stack;
48+
return new StackMiddleware($nextMiddleware);
4549
}
4650

4751
protected function getThrowingStackMock(\Throwable $throwable = null)
@@ -53,13 +57,6 @@ protected function getThrowingStackMock(\Throwable $throwable = null)
5357
->willThrowException($throwable ?? new \RuntimeException('Thrown from next middleware.'))
5458
;
5559

56-
$stack = $this->createMock(StackInterface::class);
57-
$stack
58-
->expects($this->once())
59-
->method('next')
60-
->willReturn($nextMiddleware)
61-
;
62-
63-
return $stack;
60+
return new StackMiddleware($nextMiddleware);
6461
}
6562
}
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Tests\Middleware;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Messenger\Envelope;
16+
use Symfony\Component\Messenger\MessageBus;
17+
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
18+
use Symfony\Component\Messenger\Middleware\StackInterface;
19+
use Symfony\Component\Messenger\Middleware\StackMiddleware;
20+
21+
class StackMiddlewareTest extends TestCase
22+
{
23+
public function testClone()
24+
{
25+
$middleware1 = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
26+
$middleware1
27+
->expects($this->once())
28+
->method('handle')
29+
->willReturnCallback(function (Envelope $envelope, StackInterface $stack): Envelope {
30+
$fork = clone $stack;
31+
32+
$stack->next()->handle($envelope, $stack);
33+
$fork->next()->handle($envelope, $fork);
34+
35+
return $envelope;
36+
})
37+
;
38+
39+
$middleware2 = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
40+
$middleware2
41+
->expects($this->exactly(2))
42+
->method('handle')
43+
->willReturnCallback(function (Envelope $envelope, StackInterface $stack): Envelope {
44+
return $envelope;
45+
})
46+
;
47+
48+
$bus = new MessageBus([$middleware1, $middleware2]);
49+
50+
$bus->dispatch(new \stdClass());
51+
}
52+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.