Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.x - Eliminate a duplicate code via HOF #2809

Merged
merged 5 commits into from Aug 19, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
141 changes: 77 additions & 64 deletions Slim/CallableResolver.php
Expand Up @@ -41,89 +41,94 @@ 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);
}

/**
* {@inheritdoc}
*/
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');
}

/**
* {@inheritdoc}
*/
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);
Expand All @@ -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(
Expand All @@ -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;
}
}