Completed
Push — 3.x ( c13855...2f189b )
by Sam
04:02
created

CallableResolver::assertCallable()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
dl 0
loc 9
rs 9.2
c 1
b 0
f 1
cc 4
eloc 5
nc 2
nop 1
1
<?php
2
/**
3
 * Slim Framework (https://slimframework.com)
4
 *
5
 * @link      https://github.com/slimphp/Slim
6
 * @copyright Copyright (c) 2011-2017 Josh Lockhart
7
 * @license   https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License)
8
 */
9
namespace Slim;
10
11
use RuntimeException;
12
use Psr\Container\ContainerInterface;
13
use Slim\Interfaces\CallableResolverInterface;
14
15
/**
16
 * This class resolves a string of the format 'class:method' into a closure
17
 * that can be dispatched.
18
 */
19
final class CallableResolver implements CallableResolverInterface
20
{
21
    const CALLABLE_PATTERN = '!^([^\:]+)\:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!';
22
23
    /**
24
     * @var ContainerInterface
25
     */
26
    private $container;
27
28
    /**
29
     * @param ContainerInterface $container
30
     */
31
    public function __construct(ContainerInterface $container)
32
    {
33
        $this->container = $container;
34
    }
35
36
    /**
37
     * Resolve toResolve into a closure that that the router can dispatch.
38
     *
39
     * If toResolve is of the format 'class:method', then try to extract 'class'
40
     * from the container otherwise instantiate it and then dispatch 'method'.
41
     *
42
     * @param mixed $toResolve
43
     *
44
     * @return callable
45
     *
46
     * @throws RuntimeException if the callable does not exist
47
     * @throws RuntimeException if the callable is not resolvable
48
     */
49
    public function resolve($toResolve)
50
    {
51
        if (is_callable($toResolve)) {
52
            return $toResolve;
53
        }
54
55
        if (!is_string($toResolve)) {
56
            $this->assertCallable($toResolve);
57
        }
58
59
        // check for slim callable as "class:method"
60
        if (preg_match(self::CALLABLE_PATTERN, $toResolve, $matches)) {
61
            $resolved = $this->resolveCallable($matches[1], $matches[2]);
62
            $this->assertCallable($resolved);
63
64
            return $resolved;
65
        }
66
67
        $resolved = $this->resolveCallable($toResolve);
68
        $this->assertCallable($resolved);
69
70
        return $resolved;
71
    }
72
73
    /**
74
     * Check if string is something in the DIC
75
     * that's callable or is a class name which has an __invoke() method.
76
     *
77
     * @param string $class
78
     * @param string $method
79
     * @return callable
80
     *
81
     * @throws \RuntimeException if the callable does not exist
82
     */
83
    protected function resolveCallable($class, $method = '__invoke')
84
    {
85
        if ($this->container->has($class)) {
86
            return [$this->container->get($class), $method];
87
        }
88
89
        if (!class_exists($class)) {
90
            throw new RuntimeException(sprintf('Callable %s does not exist', $class));
91
        }
92
93
        return [new $class($this->container), $method];
94
    }
95
96
    /**
97
     * @param Callable $callable
98
     *
99
     * @throws \RuntimeException if the callable is not resolvable
100
     */
101
    protected function assertCallable($callable)
102
    {
103
        if (!is_callable($callable)) {
104
            throw new RuntimeException(sprintf(
105
                '%s is not resolvable',
106
                is_array($callable) || is_object($callable) ? json_encode($callable) : $callable
107
            ));
108
        }
109
    }
110
}
111