Closed
Description
Symfony version(s) affected
6.x
Description
The StackMiddleware
has it's own state ($offset
field), which changes during the operation flow. Therefore, if at some point of time we clone this stack, we get a completely different instance, which may be safely used.
The problem lies in the absence of TraceableStack::__clone()
method .
It is not possible to use cloned stack after main stack has been iterated over, since internally it references the same object.
See provided test for details.
How to reproduce
Below two tests are provided. First passess (StackMiddleware
), second fails (TraceableStack
).
class_exists(TraceableMiddleware::class);
final class TraceableStackCloneTest extends TestCase
{
/** @dataProvider stackDataProvider */
public function testClonedStackFramesFollowOriginalFrames(StackInterface $stack): void
{
$clonedStack = clone $stack;
$nextFrame = $stack->next();
$clonedStackNextFrame = $clonedStack->next();
self::assertSame($nextFrame, $clonedStackNextFrame);
}
public function stackDataProvider(): array
{
$middlewareIterator = [
$this->createMock(MiddlewareInterface::class),
$this->createMock(MiddlewareInterface::class),
];
return [
[new StackMiddleware($middlewareIterator)], // passes
[new TraceableStack(new StackMiddleware($middlewareIterator), $this->createMock(Stopwatch::class), 'command.bus', 'test')], // fails
];
}
}
Possible Solution
I guess adding __clone
method to TraceableStack
would fix the issue:
public function __clone(): void
{
$this->stack = clone $this->stack;
}
Additional Context
PHP version: 8.2.8
Symfony messenger version: 6.2.7