diff --git a/Slim/CallableResolver.php b/Slim/CallableResolver.php index 8fc015693..71ed24dd0 100644 --- a/Slim/CallableResolver.php +++ b/Slim/CallableResolver.php @@ -41,16 +41,16 @@ public function __construct(?ContainerInterface $container = null) */ public function resolve($toResolve): callable { + if (is_callable($toResolve)) { + return $this->bindToContainer($toResolve); + } $resolved = $toResolve; - - if (!is_callable($toResolve) && is_string($toResolve)) { - $resolved = $this->resolveInstanceAndMethod($toResolve); - if ($resolved[1] === null) { - $resolved[1] = '__invoke'; - } + if (is_string($toResolve)) { + $resolved = $this->resolveSlimNotation($toResolve); + $resolved[1] = $resolved[1] ?? '__invoke'; } - - return $this->assertCallableAndBindClosureToContainer($resolved, $toResolve); + $callable = $this->assertCallable($resolved, $toResolve); + return $this->bindToContainer($callable); } /** @@ -58,25 +58,7 @@ public function resolve($toResolve): callable */ public function resolveRoute($toResolve): callable { - $resolved = $toResolve; - - if (!is_callable($toResolve) && is_string($toResolve)) { - [$instance, $method] = $this->resolveInstanceAndMethod($toResolve); - - // For a class that implements RequestHandlerInterface, we will call handle() - // if no method has been specified explicitly - if ($instance instanceof RequestHandlerInterface && $method === null) { - $method = 'handle'; - } - - $resolved = [$instance, $method ?? '__invoke']; - } - - if ($resolved instanceof RequestHandlerInterface) { - $resolved = [$resolved, 'handle']; - } - - return $this->assertCallableAndBindClosureToContainer($resolved, $toResolve); + return $this->resolveByPredicate($toResolve, [$this, 'isRoute'], 'handle'); } /** @@ -84,46 +66,69 @@ public function resolveRoute($toResolve): callable */ public function resolveMiddleware($toResolve): callable { - $resolved = $toResolve; - - if (!is_callable($toResolve) && is_string($toResolve)) { - [$instance, $method] = $this->resolveInstanceAndMethod($toResolve); + return $this->resolveByPredicate($toResolve, [$this, 'isMiddleware'], 'process'); + } - // For a class that implements MiddlewareInterface, we will call process() - // if no method has been specified explicitly - if ($instance instanceof MiddlewareInterface && $method === null) { - $method = 'process'; + /** + * @param string|callable $toResolve + * @param callable $predicate + * @param string $defaultMethod + * + * @throws RuntimeException + * + * @return callable + */ + private function resolveByPredicate($toResolve, callable $predicate, string $defaultMethod): callable + { + if (is_callable($toResolve)) { + return $this->bindToContainer($toResolve); + } + $resolved = $toResolve; + if ($predicate($toResolve)) { + $resolved = [$toResolve, $defaultMethod]; + } + if (is_string($toResolve)) { + [$instance, $method] = $this->resolveSlimNotation($toResolve); + if ($predicate($instance) && $method === null) { + $method = $defaultMethod; } - $resolved = [$instance, $method ?? '__invoke']; } + $callable = $this->assertCallable($resolved, $toResolve); + return $this->bindToContainer($callable); + } - if ($resolved instanceof MiddlewareInterface) { - $resolved = [$resolved, 'process']; - } - - return $this->assertCallableAndBindClosureToContainer($resolved, $toResolve); + /** + * @param mixed $toResolve + * + * @return bool + */ + private function isRoute($toResolve): bool + { + return $toResolve instanceof RequestHandlerInterface; } /** - * Resolves the given param and if successful returns an instance as well - * as a method name. + * @param mixed $toResolve * + * @return bool + */ + private function isMiddleware($toResolve): bool + { + return $toResolve instanceof MiddlewareInterface; + } + + /** * @param string $toResolve * + * @throws RuntimeException + * * @return array [Instance, Method Name] */ - private function resolveInstanceAndMethod(string $toResolve): array + private function resolveSlimNotation(string $toResolve): array { - $class = $toResolve; - $instance = null; - $method = null; - - // Check for Slim callable as `class:method` - if (preg_match(CallableResolver::$callablePattern, $toResolve, $matches)) { - $class = $matches[1]; - $method = $matches[2]; - } + preg_match(CallableResolver::$callablePattern, $toResolve, $matches); + [$class, $method] = $matches ? [$matches[1], $matches[2]] : [$toResolve, null]; if ($this->container && $this->container->has($class)) { $instance = $this->container->get($class); @@ -133,17 +138,18 @@ private function resolveInstanceAndMethod(string $toResolve): array } $instance = new $class($this->container); } - return [$instance, $method]; } /** - * @param mixed $resolved - * @param string|object|array|callable $toResolve + * @param mixed $resolved + * @param mixed $toResolve + * + * @throws RuntimeException * * @return callable */ - private function assertCallableAndBindClosureToContainer($resolved, $toResolve): callable + private function assertCallable($resolved, $toResolve): callable { if (!is_callable($resolved)) { throw new RuntimeException(sprintf( @@ -152,15 +158,22 @@ private function assertCallableAndBindClosureToContainer($resolved, $toResolve): json_encode($toResolve) : $toResolve )); } + return $resolved; + } - if (is_array($resolved) && $resolved[0] instanceof Closure) { - $resolved = $resolved[0]; + /** + * @param callable $callable + * + * @return callable + */ + private function bindToContainer(callable $callable): callable + { + if (is_array($callable) && $callable[0] instanceof Closure) { + $callable = $callable[0]; } - - if ($this->container && $resolved instanceof Closure) { - $resolved = $resolved->bindTo($this->container); + if ($this->container && $callable instanceof Closure) { + $callable = $callable->bindTo($this->container); } - - return $resolved; + return $callable; } }