Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
/**
4
 * Slim Framework (https://slimframework.com)
5
 *
6
 * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
7
 */
8
 
9
declare(strict_types=1);
10
 
11
namespace Slim;
12
 
13
use Closure;
14
use Psr\Container\ContainerInterface;
15
use Psr\Http\Server\MiddlewareInterface;
16
use Psr\Http\Server\RequestHandlerInterface;
17
use RuntimeException;
18
use Slim\Interfaces\AdvancedCallableResolverInterface;
19
 
20
use function class_exists;
21
use function is_array;
22
use function is_callable;
23
use function is_object;
24
use function is_string;
25
use function json_encode;
26
use function preg_match;
27
use function sprintf;
28
 
29
final class CallableResolver implements AdvancedCallableResolverInterface
30
{
31
    public static string $callablePattern = '!^([^\:]+)\:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!';
32
 
33
    private ?ContainerInterface $container;
34
 
35
    public function __construct(?ContainerInterface $container = null)
36
    {
37
        $this->container = $container;
38
    }
39
 
40
    /**
41
     * {@inheritdoc}
42
     */
43
    public function resolve($toResolve): callable
44
    {
45
        $toResolve = $this->prepareToResolve($toResolve);
46
        if (is_callable($toResolve)) {
47
            return $this->bindToContainer($toResolve);
48
        }
49
        $resolved = $toResolve;
50
        if (is_string($toResolve)) {
51
            $resolved = $this->resolveSlimNotation($toResolve);
52
            $resolved[1] ??= '__invoke';
53
        }
54
        $callable = $this->assertCallable($resolved, $toResolve);
55
        return $this->bindToContainer($callable);
56
    }
57
 
58
    /**
59
     * {@inheritdoc}
60
     */
61
    public function resolveRoute($toResolve): callable
62
    {
63
        return $this->resolveByPredicate($toResolve, [$this, 'isRoute'], 'handle');
64
    }
65
 
66
    /**
67
     * {@inheritdoc}
68
     */
69
    public function resolveMiddleware($toResolve): callable
70
    {
71
        return $this->resolveByPredicate($toResolve, [$this, 'isMiddleware'], 'process');
72
    }
73
 
74
    /**
75
     * @param string|callable $toResolve
76
     *
77
     * @throws RuntimeException
78
     */
79
    private function resolveByPredicate($toResolve, callable $predicate, string $defaultMethod): callable
80
    {
81
        $toResolve = $this->prepareToResolve($toResolve);
82
        if (is_callable($toResolve)) {
83
            return $this->bindToContainer($toResolve);
84
        }
85
        $resolved = $toResolve;
86
        if ($predicate($toResolve)) {
87
            $resolved = [$toResolve, $defaultMethod];
88
        }
89
        if (is_string($toResolve)) {
90
            [$instance, $method] = $this->resolveSlimNotation($toResolve);
91
            if ($method === null && $predicate($instance)) {
92
                $method = $defaultMethod;
93
            }
94
            $resolved = [$instance, $method ?? '__invoke'];
95
        }
96
        $callable = $this->assertCallable($resolved, $toResolve);
97
        return $this->bindToContainer($callable);
98
    }
99
 
100
    /**
101
     * @param mixed $toResolve
102
     */
103
    private function isRoute($toResolve): bool
104
    {
105
        return $toResolve instanceof RequestHandlerInterface;
106
    }
107
 
108
    /**
109
     * @param mixed $toResolve
110
     */
111
    private function isMiddleware($toResolve): bool
112
    {
113
        return $toResolve instanceof MiddlewareInterface;
114
    }
115
 
116
    /**
117
     * @throws RuntimeException
118
     *
119
     * @return array{object, string|null} [Instance, Method Name]
120
     */
121
    private function resolveSlimNotation(string $toResolve): array
122
    {
123
        preg_match(CallableResolver::$callablePattern, $toResolve, $matches);
124
        [$class, $method] = $matches ? [$matches[1], $matches[2]] : [$toResolve, null];
125
 
126
        /** @var string $class */
127
        /** @var string|null $method */
128
        if ($this->container && $this->container->has($class)) {
129
            $instance = $this->container->get($class);
130
            if (!is_object($instance)) {
131
                throw new RuntimeException(sprintf('%s container entry is not an object', $class));
132
            }
133
        } else {
134
            if (!class_exists($class)) {
135
                if ($method) {
136
                    $class .= '::' . $method . '()';
137
                }
138
                throw new RuntimeException(sprintf('Callable %s does not exist', $class));
139
            }
140
            $instance = new $class($this->container);
141
        }
142
        return [$instance, $method];
143
    }
144
 
145
    /**
146
     * @param mixed $resolved
147
     * @param mixed $toResolve
148
     *
149
     * @throws RuntimeException
150
     */
151
    private function assertCallable($resolved, $toResolve): callable
152
    {
153
        if (!is_callable($resolved)) {
154
            if (is_callable($toResolve) || is_object($toResolve) || is_array($toResolve)) {
155
                $formatedToResolve = ($toResolveJson = json_encode($toResolve)) !== false ? $toResolveJson : '';
156
            } else {
157
                $formatedToResolve = is_string($toResolve) ? $toResolve : '';
158
            }
159
            throw new RuntimeException(sprintf('%s is not resolvable', $formatedToResolve));
160
        }
161
        return $resolved;
162
    }
163
 
164
    private function bindToContainer(callable $callable): callable
165
    {
166
        if (is_array($callable) && $callable[0] instanceof Closure) {
167
            $callable = $callable[0];
168
        }
169
        if ($this->container && $callable instanceof Closure) {
170
            /** @var Closure $callable */
171
            $callable = $callable->bindTo($this->container);
172
        }
173
        return $callable;
174
    }
175
 
176
    /**
177
     * @param string|callable $toResolve
178
     * @return string|callable
179
     */
180
    private function prepareToResolve($toResolve)
181
    {
182
        if (!is_array($toResolve)) {
183
            return $toResolve;
184
        }
185
        $candidate = $toResolve;
186
        $class = array_shift($candidate);
187
        $method = array_shift($candidate);
188
        if (is_string($class) && is_string($method)) {
189
            return $class . ':' . $method;
190
        }
191
        return $toResolve;
192
    }
193
}