order[] = 'm1_start'; $res = $next($c, $i, $o); $this->order[] = 'm1_end'; return $res; } }; $m2 = new class($order) implements ConsoleMiddlewareInterface { public function __construct(private array &$order) {} public function handle(CommandInterface $c, InputInterface $i, OutputInterface $o, callable $next): int { $this->order[] = 'm2_start'; $res = $next($c, $i, $o); $this->order[] = 'm2_end'; return $res; } }; $stack = new MiddlewareStack([$m1, $m2]); $command = $this->createMock(CommandInterface::class); $input = $this->createMock(InputInterface::class); $output = $this->createMock(OutputInterface::class); $stack->handle($command, $input, $output, function() use (&$order) { $order[] = 'command'; return 0; }); $this->assertEquals(['m1_start', 'm2_start', 'command', 'm2_end', 'm1_end'], $order); } public function testMiddlewareEarlyReturn(): void { $m1 = new class implements ConsoleMiddlewareInterface { public function handle(CommandInterface $c, InputInterface $i, OutputInterface $o, callable $next): int { return 42; } }; $commandExecuted = false; $stack = new MiddlewareStack([$m1]); $res = $stack->handle( $this->createMock(CommandInterface::class), $this->createMock(InputInterface::class), $this->createMock(OutputInterface::class), function() use (&$commandExecuted) { $commandExecuted = true; return 0; } ); $this->assertEquals(42, $res); $this->assertFalse($commandExecuted); } public function testExceptionGuardMiddleware(): void { $output = $this->createMock(OutputInterface::class); $output->expects($this->once())->method('error')->with($this->stringContains('Test exception')); $m = new ExceptionGuardMiddleware(); $res = $m->handle( $this->createMock(CommandInterface::class), $this->createMock(InputInterface::class), $output, function() { throw new Exception('Test exception'); } ); $this->assertEquals(ExitCode::SOFTWARE, $res); } }