Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit ccb94de

Browse files
committed
Merge branch 'hotfix/527-pipe-with-something-looking-callable'
Close #534 Fixes #527
2 parents b404854 + a90fe56 commit ccb94de

File tree

4 files changed

+62
-2
lines changed

4 files changed

+62
-2
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ All notable changes to this project will be documented in this file, in reverse
3030
requires it. This addition fixes problems due to missing http-middleware
3131
interfaces.
3232

33+
- [#534](https://github.com/zendframework/zend-expressive/pull/534) provides a
34+
fix for how it detects `callable` middleware. Previously, it relied on PHP's
35+
`is_callable()`, but that function can result in false positives when provided
36+
a 2-element array where the first element is an object, as the function does
37+
not verify that the second argument is a valid method of the first. We now
38+
implement additional verifications to prevent such false positives.
39+
3340
## 2.0.4 - 2017-10-09
3441

3542
### Added

src/IsCallableInteropMiddlewareTrait.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,35 @@
1313

1414
trait IsCallableInteropMiddlewareTrait
1515
{
16+
/**
17+
* Is the provided $middleware argument callable?
18+
*
19+
* Runs the argument against is_callable(). If that returns true, and the
20+
* value is an array with two elements, tests to ensure that the second
21+
* element is a method of the first.
22+
*
23+
* @param mixed $middleware
24+
* @return bool
25+
*/
26+
private function isCallable($middleware)
27+
{
28+
if (! is_callable($middleware)) {
29+
return false;
30+
}
31+
32+
if (! is_array($middleware)) {
33+
return true;
34+
}
35+
36+
$classOrObject = array_shift($middleware);
37+
if (! is_object($classOrObject) && ! class_exists($classOrObject)) {
38+
return false;
39+
}
40+
41+
$method = array_shift($middleware);
42+
return method_exists($classOrObject, $method);
43+
}
44+
1645
/**
1746
* Is callable middleware interop middleware?
1847
*
@@ -21,7 +50,7 @@ trait IsCallableInteropMiddlewareTrait
2150
*/
2251
private function isCallableInteropMiddleware($middleware)
2352
{
24-
if (! is_callable($middleware)) {
53+
if (! $this->isCallable($middleware)) {
2554
return false;
2655
}
2756

src/MarshalMiddlewareTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ private function prepareMiddleware(
6363
return new CallableInteropMiddlewareWrapper($middleware);
6464
}
6565

66-
if (is_callable($middleware)) {
66+
if ($this->isCallable($middleware)) {
6767
return new CallableMiddlewareWrapper($middleware, $responsePrototype);
6868
}
6969

test/ApplicationTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use DomainException;
1212
use Fig\Http\Message\StatusCodeInterface as StatusCode;
1313
use Interop\Http\ServerMiddleware\DelegateInterface;
14+
use Interop\Http\ServerMiddleware\MiddlewareInterface;
1415
use InvalidArgumentException;
1516
use Mockery;
1617
use PHPUnit\Framework\TestCase;
@@ -19,6 +20,7 @@
1920
use Psr\Container\ContainerInterface;
2021
use Psr\Http\Message\ResponseInterface;
2122
use Psr\Http\Message\ServerRequestInterface;
23+
use ReflectionMethod;
2224
use ReflectionProperty;
2325
use RuntimeException;
2426
use UnexpectedValueException;
@@ -720,4 +722,26 @@ public function testAllowsNestedMiddlewarePipelines()
720722

721723
$this->assertSame($response, $app->process($request, $delegate->reveal()));
722724
}
725+
726+
public function testPreparingArrayWithPairOfObjectAndStringMiddlewaresShouldNotBeTreatedAsCallable()
727+
{
728+
$first = $this->prophesize(MiddlewareInterface::class)->reveal();
729+
$second = TestAsset\CallableInteropMiddleware::class;
730+
$queue = [$first, $second];
731+
732+
$router = $this->router->reveal();
733+
$response = $this->prophesize(ResponseInterface::class)->reveal();
734+
735+
$app = $this->getApp();
736+
$r = new ReflectionMethod($app, 'prepareMiddleware');
737+
$r->setAccessible(true);
738+
739+
$middleware = $r->invoke($app, $queue, $router, $response);
740+
741+
$this->assertInstanceOf(MiddlewarePipe::class, $middleware);
742+
743+
$r = new ReflectionProperty($middleware, 'pipeline');
744+
$r->setAccessible(true);
745+
$this->assertCount(2, $r->getValue($middleware));
746+
}
723747
}

0 commit comments

Comments
 (0)