zendframework / zend-expressive

PSR-15 middleware in minutes!
BSD 3-Clause "New" or "Revised" License
711 stars 197 forks source link

$app->pipe([$obj, 'string']) fails #527

Closed danizord closed 6 years ago

danizord commented 6 years ago

When writing tests for #525, I've realised that Expressive is failing when piping two middlewares like that:

$app->pipe([
    new SomeMiddleware(),
    SomeOtherMiddleware::class,
]);

Since is_callable() returns true for [$object, 'string'], Expressive is trying to handle it as a callable interop middleware, then it breaks at https://github.com/zendframework/zend-expressive/blob/2.0.5/src/IsCallableInteropMiddlewareTrait.php#L46-L48.

Here's the failing testcase:

// test/ApplicationTest.php
public function testPreparingArrayWithPairOfObjectAndStringMiddlewaresShouldNotBeTreatedAsCallable() {
    $first  = $this->prophesize(ServerMiddlewareInterface::class)->reveal();
    $second = TestAsset\CallableInteropMiddleware::class;

    $base = [$first, $second];

    $middleware = $this->prepareMiddleware($base);
    $this->assertInstanceOf(MiddlewarePipe::class, $middleware);

    $r = new ReflectionProperty($middleware, 'pipeline');
    $r->setAccessible(true);

    $this->assertCount(2, $r->getValue($middleware));
}
weierophinney commented 6 years ago

Verified. I had to make a few changes to your proposed test case, as prepareMiddleware() is a method of Application, not the test case; it is, in fact, private (requiring reflection); and it has two more required arguments. Final test case looks like this:

    public function testPreparingArrayWithPairOfObjectAndStringMiddlewaresShouldNotBeTreatedAsCallable()
    {
        $first  = $this->prophesize(MiddlewareInterface::class)->reveal();
        $second = TestAsset\CallableInteropMiddleware::class;
        $queue  = [$first, $second];

        $router = $this->router->reveal();
        $response = $this->prophesize(ResponseInterface::class)->reveal();

        $app = $this->getApp();
        $r = new ReflectionMethod($app, 'prepareMiddleware');
        $r->setAccessible(true);

        $middleware = $r->invoke($app, $queue, $router, $response);

        $this->assertInstanceOf(MiddlewarePipe::class, $middleware);

        $r = new ReflectionProperty($middleware, 'pipeline');
        $r->setAccessible(true);
        $this->assertCount(2, $r->getValue($middleware));
    }

That fails, indicating that a method by the name of Double\MiddlewareInterface\P213::ZendTest\Expressive\TestAsset\CallableInteropMiddleware() does not exist.