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\Message\ResponseInterface;
16
use Psr\Http\Message\ServerRequestInterface;
17
use Psr\Http\Server\MiddlewareInterface;
18
use Psr\Http\Server\RequestHandlerInterface;
19
use RuntimeException;
20
use Slim\Interfaces\AdvancedCallableResolverInterface;
21
use Slim\Interfaces\CallableResolverInterface;
22
use Slim\Interfaces\MiddlewareDispatcherInterface;
23
 
24
use function class_exists;
25
use function function_exists;
26
use function is_callable;
27
use function is_string;
28
use function preg_match;
29
use function sprintf;
30
 
31
class MiddlewareDispatcher implements MiddlewareDispatcherInterface
32
{
33
    /**
34
     * Tip of the middleware call stack
35
     */
36
    protected RequestHandlerInterface $tip;
37
 
38
    protected ?CallableResolverInterface $callableResolver;
39
 
40
    protected ?ContainerInterface $container;
41
 
42
    public function __construct(
43
        RequestHandlerInterface $kernel,
44
        ?CallableResolverInterface $callableResolver = null,
45
        ?ContainerInterface $container = null
46
    ) {
47
        $this->seedMiddlewareStack($kernel);
48
        $this->callableResolver = $callableResolver;
49
        $this->container = $container;
50
    }
51
 
52
    /**
53
     * {@inheritdoc}
54
     */
55
    public function seedMiddlewareStack(RequestHandlerInterface $kernel): void
56
    {
57
        $this->tip = $kernel;
58
    }
59
 
60
    /**
61
     * Invoke the middleware stack
62
     */
63
    public function handle(ServerRequestInterface $request): ResponseInterface
64
    {
65
        return $this->tip->handle($request);
66
    }
67
 
68
    /**
69
     * Add a new middleware to the stack
70
     *
71
     * Middleware are organized as a stack. That means middleware
72
     * that have been added before will be executed after the newly
73
     * added one (last in, first out).
74
     *
75
     * @param MiddlewareInterface|string|callable $middleware
76
     */
77
    public function add($middleware): MiddlewareDispatcherInterface
78
    {
79
        if ($middleware instanceof MiddlewareInterface) {
80
            return $this->addMiddleware($middleware);
81
        }
82
 
83
        if (is_string($middleware)) {
84
            return $this->addDeferred($middleware);
85
        }
86
 
87
        if (is_callable($middleware)) {
88
            return $this->addCallable($middleware);
89
        }
90
 
91
        /** @phpstan-ignore-next-line */
92
        throw new RuntimeException(
93
            'A middleware must be an object/class name referencing an implementation of ' .
94
            'MiddlewareInterface or a callable with a matching signature.'
95
        );
96
    }
97
 
98
    /**
99
     * Add a new middleware to the stack
100
     *
101
     * Middleware are organized as a stack. That means middleware
102
     * that have been added before will be executed after the newly
103
     * added one (last in, first out).
104
     */
105
    public function addMiddleware(MiddlewareInterface $middleware): MiddlewareDispatcherInterface
106
    {
107
        $next = $this->tip;
108
        $this->tip = new class ($middleware, $next) implements RequestHandlerInterface {
109
            private MiddlewareInterface $middleware;
110
 
111
            private RequestHandlerInterface $next;
112
 
113
            public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $next)
114
            {
115
                $this->middleware = $middleware;
116
                $this->next = $next;
117
            }
118
 
119
            public function handle(ServerRequestInterface $request): ResponseInterface
120
            {
121
                return $this->middleware->process($request, $this->next);
122
            }
123
        };
124
 
125
        return $this;
126
    }
127
 
128
    /**
129
     * Add a new middleware by class name
130
     *
131
     * Middleware are organized as a stack. That means middleware
132
     * that have been added before will be executed after the newly
133
     * added one (last in, first out).
134
     */
135
    public function addDeferred(string $middleware): self
136
    {
137
        $next = $this->tip;
138
        $this->tip = new class (
139
            $middleware,
140
            $next,
141
            $this->container,
142
            $this->callableResolver
143
        ) implements RequestHandlerInterface {
144
            private string $middleware;
145
 
146
            private RequestHandlerInterface $next;
147
 
148
            private ?ContainerInterface $container;
149
 
150
            private ?CallableResolverInterface $callableResolver;
151
 
152
            public function __construct(
153
                string $middleware,
154
                RequestHandlerInterface $next,
155
                ?ContainerInterface $container = null,
156
                ?CallableResolverInterface $callableResolver = null
157
            ) {
158
                $this->middleware = $middleware;
159
                $this->next = $next;
160
                $this->container = $container;
161
                $this->callableResolver = $callableResolver;
162
            }
163
 
164
            public function handle(ServerRequestInterface $request): ResponseInterface
165
            {
166
                if ($this->callableResolver instanceof AdvancedCallableResolverInterface) {
167
                    $callable = $this->callableResolver->resolveMiddleware($this->middleware);
168
                    return $callable($request, $this->next);
169
                }
170
 
171
                $callable = null;
172
 
173
                if ($this->callableResolver instanceof CallableResolverInterface) {
174
                    try {
175
                        $callable = $this->callableResolver->resolve($this->middleware);
176
                    } catch (RuntimeException $e) {
177
                        // Do Nothing
178
                    }
179
                }
180
 
181
                if (!$callable) {
182
                    $resolved = $this->middleware;
183
                    $instance = null;
184
                    $method = null;
185
 
186
                    // Check for Slim callable as `class:method`
187
                    if (preg_match(CallableResolver::$callablePattern, $resolved, $matches)) {
188
                        $resolved = $matches[1];
189
                        $method = $matches[2];
190
                    }
191
 
192
                    if ($this->container && $this->container->has($resolved)) {
193
                        $instance = $this->container->get($resolved);
194
                        if ($instance instanceof MiddlewareInterface) {
195
                            return $instance->process($request, $this->next);
196
                        }
197
                    } elseif (!function_exists($resolved)) {
198
                        if (!class_exists($resolved)) {
199
                            throw new RuntimeException(sprintf('Middleware %s does not exist', $resolved));
200
                        }
201
                        $instance = new $resolved($this->container);
202
                    }
203
 
204
                    if ($instance && $instance instanceof MiddlewareInterface) {
205
                        return $instance->process($request, $this->next);
206
                    }
207
 
208
                    $callable = $instance ?? $resolved;
209
                    if ($instance && $method) {
210
                        $callable = [$instance, $method];
211
                    }
212
 
213
                    if ($this->container && $callable instanceof Closure) {
214
                        $callable = $callable->bindTo($this->container);
215
                    }
216
                }
217
 
218
                if (!is_callable($callable)) {
219
                    throw new RuntimeException(
220
                        sprintf(
221
                            'Middleware %s is not resolvable',
222
                            $this->middleware
223
                        )
224
                    );
225
                }
226
 
227
                return $callable($request, $this->next);
228
            }
229
        };
230
 
231
        return $this;
232
    }
233
 
234
    /**
235
     * Add a (non-standard) callable middleware to the stack
236
     *
237
     * Middleware are organized as a stack. That means middleware
238
     * that have been added before will be executed after the newly
239
     * added one (last in, first out).
240
     */
241
    public function addCallable(callable $middleware): self
242
    {
243
        $next = $this->tip;
244
 
245
        if ($this->container && $middleware instanceof Closure) {
246
            /** @var Closure $middleware */
247
            $middleware = $middleware->bindTo($this->container);
248
        }
249
 
250
        $this->tip = new class ($middleware, $next) implements RequestHandlerInterface {
251
            /**
252
             * @var callable
253
             */
254
            private $middleware;
255
 
256
            /**
257
             * @var RequestHandlerInterface
258
             */
259
            private $next;
260
 
261
            public function __construct(callable $middleware, RequestHandlerInterface $next)
262
            {
263
                $this->middleware = $middleware;
264
                $this->next = $next;
265
            }
266
 
267
            public function handle(ServerRequestInterface $request): ResponseInterface
268
            {
269
                return ($this->middleware)($request, $this->next);
270
            }
271
        };
272
 
273
        return $this;
274
    }
275
}