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

Commit 403505b

Browse files
committed
Merge branch 'hotfix/527-pipe-with-something-looking-callable' into develop
Forward port #534 Conflicts: test/ApplicationTest.php
2 parents 60f145d + a90fe56 commit 403505b

File tree

4 files changed

+63
-2
lines changed

4 files changed

+63
-2
lines changed

CHANGELOG.md

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

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

8289
### 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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
use DomainException;
1111
use Fig\Http\Message\StatusCodeInterface as StatusCode;
12+
use Interop\Http\ServerMiddleware\DelegateInterface;
13+
use Interop\Http\ServerMiddleware\MiddlewareInterface;
1214
use InvalidArgumentException;
1315
use Mockery;
1416
use PHPUnit\Framework\TestCase;
@@ -17,6 +19,7 @@
1719
use Psr\Container\ContainerInterface;
1820
use Psr\Http\Message\ResponseInterface;
1921
use Psr\Http\Message\ServerRequestInterface;
22+
use ReflectionMethod;
2023
use ReflectionProperty;
2124
use RuntimeException;
2225
use UnexpectedValueException;
@@ -707,4 +710,26 @@ public function testAllowsNestedMiddlewarePipelines()
707710

708711
$this->assertSame($response, $app->process($request, $delegate->reveal()));
709712
}
713+
714+
public function testPreparingArrayWithPairOfObjectAndStringMiddlewaresShouldNotBeTreatedAsCallable()
715+
{
716+
$first = $this->prophesize(MiddlewareInterface::class)->reveal();
717+
$second = TestAsset\CallableInteropMiddleware::class;
718+
$queue = [$first, $second];
719+
720+
$router = $this->router->reveal();
721+
$response = $this->prophesize(ResponseInterface::class)->reveal();
722+
723+
$app = $this->getApp();
724+
$r = new ReflectionMethod($app, 'prepareMiddleware');
725+
$r->setAccessible(true);
726+
727+
$middleware = $r->invoke($app, $queue, $router, $response);
728+
729+
$this->assertInstanceOf(MiddlewarePipe::class, $middleware);
730+
731+
$r = new ReflectionProperty($middleware, 'pipeline');
732+
$r->setAccessible(true);
733+
$this->assertCount(2, $r->getValue($middleware));
734+
}
710735
}

0 commit comments

Comments
 (0)