diff --git a/src/Symfony/Component/Messenger/Middleware/TraceableMiddleware.php b/src/Symfony/Component/Messenger/Middleware/TraceableMiddleware.php index 075916d7823e4..68ddeb424539b 100644 --- a/src/Symfony/Component/Messenger/Middleware/TraceableMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/TraceableMiddleware.php @@ -88,4 +88,9 @@ public function stop(): void } $this->currentEvent = null; } + + public function __clone(): void + { + $this->stack = clone $this->stack; + } } diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/TraceableMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/TraceableMiddlewareTest.php index a0006bbf05e07..1cea529a5840d 100644 --- a/src/Symfony/Component/Messenger/Tests/Middleware/TraceableMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Middleware/TraceableMiddlewareTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Messenger\Middleware\StackInterface; use Symfony\Component\Messenger\Middleware\StackMiddleware; use Symfony\Component\Messenger\Middleware\TraceableMiddleware; +use Symfony\Component\Messenger\Middleware\TraceableStack; use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Stopwatch\Stopwatch; @@ -140,4 +141,92 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope $traced->handle($envelope, new StackMiddleware(new \ArrayIterator([null, $middleware]))); $this->assertSame(1, $middleware->calls); } + + public function testClonedTraceableStackUnstacksIndependently(): void + { + // import TraceableStack + class_exists(TraceableMiddleware::class); + + $stackMiddleware = new StackMiddleware([ + null, + $this->createMock(MiddlewareInterface::class), + $this->createMock(MiddlewareInterface::class), + ]); + + $stopwatch = $this->createMock(Stopwatch::class); + + $traceableStack = new TraceableStack($stackMiddleware, $stopwatch, 'command_bus', 'messenger.middleware'); + $clonedStack = clone $traceableStack; + + $traceableStackMiddleware1 = $traceableStack->next(); + $traceableStackMiddleware2 = $traceableStack->next(); + $traceableStackTail = $traceableStack->next(); + self::assertSame($stackMiddleware, $traceableStackTail); + + // unstack clonedStack independently + $clonedStackMiddleware1 = $clonedStack->next(); + self::assertSame($traceableStackMiddleware1, $clonedStackMiddleware1); + self::assertNotSame($traceableStackMiddleware2, $clonedStackMiddleware1); + + $clonedStackMiddleware2 = $clonedStack->next(); + self::assertSame($traceableStackMiddleware2, $clonedStackMiddleware2); + + $clonedStackTail = $clonedStack->next(); + self::assertNotSame($stackMiddleware, $clonedStackTail, 'stackMiddleware was also cloned'); + } + + public function testClonedTraceableStackUsesSameStopwatch(): void + { + // import TraceableStack + class_exists(TraceableMiddleware::class); + + $middlewareIterable = [null, $this->createMock(MiddlewareInterface::class)]; + + $stackMiddleware = new StackMiddleware($middlewareIterable); + + $stopwatch = $this->createMock(Stopwatch::class); + $stopwatch->expects($this->exactly(2))->method('isStarted')->willReturn(true); + + $startSeries = [ + [$this->matches('"%sMiddlewareInterface%s" on "command_bus"'), 'messenger.middleware'], + [$this->identicalTo('Tail on "command_bus"'), 'messenger.middleware'], + [$this->matches('"%sMiddlewareInterface%s" on "command_bus"'), 'messenger.middleware'], + [$this->identicalTo('Tail on "command_bus"'), 'messenger.middleware'], + ]; + $stopwatch->expects($this->exactly(4)) + ->method('start') + ->willReturnCallback(function (string $name, string $category = null) use (&$startSeries) { + [$constraint, $expectedCategory] = array_shift($startSeries); + + $constraint->evaluate($name); + $this->assertSame($expectedCategory, $category); + + return $this->createMock(StopwatchEvent::class); + }) + ; + + $stopSeries = [ + $this->matches('"%sMiddlewareInterface%s" on "command_bus"'), + $this->matches('"%sMiddlewareInterface%s" on "command_bus"'), + ]; + $stopwatch->expects($this->exactly(2)) + ->method('stop') + ->willReturnCallback(function (string $name) use (&$stopSeries) { + $constraint = array_shift($stopSeries); + $constraint->evaluate($name); + + return $this->createMock(StopwatchEvent::class); + }) + ; + + $traceableStack = new TraceableStack($stackMiddleware, $stopwatch, 'command_bus', 'messenger.middleware'); + $clonedStack = clone $traceableStack; + + // unstack the stacks independently + $traceableStack->next(); + $traceableStack->next(); + + $clonedStack->next(); + $clonedStack->next(); + } }