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

Commit 63fe808

Browse files
committed
Provides more robust is_callable logic
Provides a new method, `IsCallableInteropMiddlewareTrait::isCallable()`, which runs its argument through `is_callable()`. If the value passes, it then checks to see if it is an array, and, if so, if: - the first argument is an object or valid class name, AND - the second argument is a valid method of the first argument. This is done because `is_callable()` returns a false positive when a two-element array is passed where the first argument is an object and the second a string; the function does not verify that the second element is a valid method of the first.
1 parent b404854 commit 63fe808

File tree

3 files changed

+55
-2
lines changed

3 files changed

+55
-2
lines changed

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)