diff --git a/Slim/CallableResolver.php b/Slim/CallableResolver.php index f2c5fe4cd..c1c351276 100644 --- a/Slim/CallableResolver.php +++ b/Slim/CallableResolver.php @@ -51,6 +51,7 @@ public function __construct(?ContainerInterface $container = null) */ public function resolve($toResolve): callable { + $toResolve = $this->prepareToResolve($toResolve); if (is_callable($toResolve)) { return $this->bindToContainer($toResolve); } @@ -90,6 +91,7 @@ public function resolveMiddleware($toResolve): callable */ private function resolveByPredicate($toResolve, callable $predicate, string $defaultMethod): callable { + $toResolve = $this->prepareToResolve($toResolve); if (is_callable($toResolve)) { return $this->bindToContainer($toResolve); } @@ -144,6 +146,9 @@ private function resolveSlimNotation(string $toResolve): array $instance = $this->container->get($class); } else { if (!class_exists($class)) { + if ($method) { + $class .= '::' . $method . '()'; + } throw new RuntimeException(sprintf('Callable %s does not exist', $class)); } $instance = new $class($this->container); @@ -187,4 +192,22 @@ private function bindToContainer(callable $callable): callable } return $callable; } + + /** + * @param string|callable $toResolve + * @return string|callable + */ + private function prepareToResolve($toResolve) + { + if (!is_array($toResolve)) { + return $toResolve; + } + $candidate = $toResolve; + $class = array_shift($candidate); + $method = array_shift($candidate); + if (is_string($class) && is_string($method)) { + return $class . ':' . $method; + } + return $toResolve; + } } diff --git a/tests/CallableResolverTest.php b/tests/CallableResolverTest.php index 727164c48..27e0823a5 100644 --- a/tests/CallableResolverTest.php +++ b/tests/CallableResolverTest.php @@ -133,6 +133,23 @@ public function testSlimCallable() $this->assertEquals(3, CallableTest::$CalledCount); } + public function testSlimCallableAsArray() + { + $resolver = new CallableResolver(); // No container injected + $callable = $resolver->resolve([CallableTest::class, 'toCall']); + $callableRoute = $resolver->resolveRoute([CallableTest::class, 'toCall']); + $callableMiddleware = $resolver->resolveMiddleware([CallableTest::class, 'toCall']); + + $callable(); + $this->assertEquals(1, CallableTest::$CalledCount); + + $callableRoute(); + $this->assertEquals(2, CallableTest::$CalledCount); + + $callableMiddleware(); + $this->assertEquals(3, CallableTest::$CalledCount); + } + public function testSlimCallableContainer() { /** @var ContainerInterface $container */ @@ -150,6 +167,23 @@ public function testSlimCallableContainer() $this->assertEquals($container, CallableTest::$CalledContainer); } + public function testSlimCallableAsArrayContainer() + { + /** @var ContainerInterface $container */ + $container = $this->containerProphecy->reveal(); + $resolver = new CallableResolver($container); + $resolver->resolve([CallableTest::class, 'toCall']); + $this->assertEquals($container, CallableTest::$CalledContainer); + + CallableTest::$CalledContainer = null; + $resolver->resolveRoute([CallableTest::class, 'toCall']); + $this->assertEquals($container, CallableTest::$CalledContainer); + + CallableTest::$CalledContainer = null; + $resolver->resolveMiddleware([CallableTest::class ,'toCall']); + $this->assertEquals($container, CallableTest::$CalledContainer); + } + public function testContainer() { $this->containerProphecy->has('callable_service')->willReturn(true); @@ -441,7 +475,7 @@ public function testMiddlewareFunctionNotFoundThrowException() public function testClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Callable Unknown does not exist'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -452,7 +486,7 @@ public function testClassNotFoundThrowException() public function testRouteClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Callable Unknown does not exist'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -463,7 +497,7 @@ public function testRouteClassNotFoundThrowException() public function testMiddlewareClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Callable Unknown does not exist'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -474,7 +508,7 @@ public function testMiddlewareClassNotFoundThrowException() public function testCallableClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('is not resolvable'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -485,7 +519,7 @@ public function testCallableClassNotFoundThrowException() public function testRouteCallableClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('is not resolvable'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -496,7 +530,7 @@ public function testRouteCallableClassNotFoundThrowException() public function testMiddlewareCallableClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('is not resolvable'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal();