• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IlyasDeckers / ody-core / 13532154862

25 Feb 2025 10:24PM UTC coverage: 30.374% (+1.7%) from 28.706%
13532154862

push

github

web-flow
Update php.yml

544 of 1791 relevant lines covered (30.37%)

9.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

28.41
/src/MiddlewareDispatcher.php
1
<?php
2
declare(strict_types=1);
3

4
namespace Ody\Core;
5

6
use Closure;
7
use Ody\Core\DI\CallableResolver;
8
use Ody\Core\Interfaces\AdvancedCallableResolverInterface;
9
use Ody\Core\Interfaces\CallableResolverInterface;
10
use Ody\Core\Interfaces\MiddlewareDispatcherInterface;
11
use Psr\Container\ContainerInterface;
12
use Psr\Http\Message\ResponseInterface;
13
use Psr\Http\Message\ServerRequestInterface;
14
use Psr\Http\Server\MiddlewareInterface;
15
use Psr\Http\Server\RequestHandlerInterface;
16
use RuntimeException;
17
use function class_exists;
18
use function function_exists;
19
use function is_callable;
20
use function is_string;
21
use function preg_match;
22
use function sprintf;
23

24
/**
25
 * @api
26
 * @template TContainerInterface of (ContainerInterface|null)
27
 */
28
class MiddlewareDispatcher implements MiddlewareDispatcherInterface
29
{
30
    /**
31
     * Tip of the middleware call stack
32
     */
33
    protected RequestHandlerInterface $tip;
34

35
    protected ?CallableResolverInterface $callableResolver;
36

37
    /** @var TContainerInterface $container */
38
    protected ?ContainerInterface $container;
39

40
    /**
41
     * @param TContainerInterface $container
42
     */
43
    public function __construct(
98✔
44
        RequestHandlerInterface $kernel,
45
        ?CallableResolverInterface $callableResolver = null,
46
        ?ContainerInterface $container = null
47
    ) {
48
        $this->seedMiddlewareStack($kernel);
98✔
49
        $this->callableResolver = $callableResolver;
98✔
50
        $this->container = $container;
98✔
51
    }
52

53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function seedMiddlewareStack(RequestHandlerInterface $kernel): void
98✔
57
    {
58
        $this->tip = $kernel;
98✔
59
    }
60

61
    /**
62
     * Invoke the middleware stack
63
     */
64
    public function handle(ServerRequestInterface $request): ResponseInterface
45✔
65
    {
66
        return $this->tip->handle($request);
45✔
67
    }
68

69
    /**
70
     * Add a new middleware to the stack
71
     *
72
     * Middleware are organized as a stack. That means middleware
73
     * that have been added before will be executed after the newly
74
     * added one (last in, first out).
75
     *
76
     * @param MiddlewareInterface|string|callable $middleware
77
     */
78
    public function add($middleware): MiddlewareDispatcherInterface
9✔
79
    {
80
        if ($middleware instanceof MiddlewareInterface) {
9✔
81
            return $this->addMiddleware($middleware);
8✔
82
        }
83

84
        if (is_string($middleware)) {
1✔
85
            return $this->addDeferred($middleware);
×
86
        }
87

88
        if (is_callable($middleware)) {
1✔
89
            return $this->addCallable($middleware);
×
90
        }
91

92
        /** @phpstan-ignore-next-line */
93
        throw new RuntimeException(
1✔
94
            'A middleware must be an object/class name referencing an implementation of ' .
1✔
95
            'MiddlewareInterface or a callable with a matching signature.'
1✔
96
        );
1✔
97
    }
98

99
    /**
100
     * Add a new middleware to the stack
101
     *
102
     * Middleware are organized as a stack. That means middleware
103
     * that have been added before will be executed after the newly
104
     * added one (last in, first out).
105
     */
106
    public function addMiddleware(MiddlewareInterface $middleware): MiddlewareDispatcherInterface
8✔
107
    {
108
        $next = $this->tip;
8✔
109
        $this->tip = new class ($middleware, $next) implements RequestHandlerInterface {
8✔
110
            private MiddlewareInterface $middleware;
111

112
            private RequestHandlerInterface $next;
113

114
            public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $next)
115
            {
116
                $this->middleware = $middleware;
8✔
117
                $this->next = $next;
8✔
118
            }
119

120
            public function handle(ServerRequestInterface $request): ResponseInterface
121
            {
122
                return $this->middleware->process($request, $this->next);
5✔
123
            }
124
        };
8✔
125

126
        return $this;
8✔
127
    }
128

129
    /**
130
     * Add a new middleware by class name
131
     *
132
     * Middleware are organized as a stack. That means middleware
133
     * that have been added before will be executed after the newly
134
     * added one (last in, first out).
135
     * @return MiddlewareDispatcher<TContainerInterface>
136
     */
137
    public function addDeferred(string $middleware): self
×
138
    {
139
        $next = $this->tip;
×
140
        $this->tip = new class (
×
141
            $middleware,
×
142
            $next,
×
143
            $this->container,
×
144
            $this->callableResolver
×
145
        ) implements RequestHandlerInterface {
×
146
            private string $middleware;
147

148
            private RequestHandlerInterface $next;
149

150
            private ?ContainerInterface $container;
151

152
            private ?CallableResolverInterface $callableResolver;
153

154
            public function __construct(
155
                string $middleware,
156
                RequestHandlerInterface $next,
157
                ?ContainerInterface $container = null,
158
                ?CallableResolverInterface $callableResolver = null
159
            ) {
160
                $this->middleware = $middleware;
×
161
                $this->next = $next;
×
162
                $this->container = $container;
×
163
                $this->callableResolver = $callableResolver;
×
164
            }
165

166
            public function handle(ServerRequestInterface $request): ResponseInterface
167
            {
168
                if ($this->callableResolver instanceof AdvancedCallableResolverInterface) {
×
169
                    $callable = $this->callableResolver->resolveMiddleware($this->middleware);
×
170
                    return $callable($request, $this->next);
×
171
                }
172

173
                $callable = null;
×
174

175
                if ($this->callableResolver instanceof CallableResolverInterface) {
×
176
                    try {
177
                        $callable = $this->callableResolver->resolve($this->middleware);
×
178
                    } catch (RuntimeException $e) {
×
179
                        // Do Nothing
180
                    }
181
                }
182

183
                if (!$callable) {
×
184
                    $resolved = $this->middleware;
×
185
                    $instance = null;
×
186
                    $method = null;
×
187

188
                    /** @psalm-suppress ArgumentTypeCoercion */
189
                    // Check for Slim callable as `class:method`
190
                    if (preg_match(CallableResolver::$callablePattern, $resolved, $matches)) {
×
191
                        $resolved = $matches[1];
×
192
                        $method = $matches[2];
×
193
                    }
194

195
                    if ($this->container && $this->container->has($resolved)) {
×
196
                        $instance = $this->container->get($resolved);
×
197
                        if ($instance instanceof MiddlewareInterface) {
×
198
                            return $instance->process($request, $this->next);
×
199
                        }
200
                    } elseif (!function_exists($resolved)) {
×
201
                        if (!class_exists($resolved)) {
×
202
                            throw new RuntimeException(sprintf('Middleware %s does not exist', $resolved));
×
203
                        }
204
                        $instance = new $resolved($this->container);
×
205
                    }
206

207
                    if ($instance instanceof MiddlewareInterface) {
×
208
                        return $instance->process($request, $this->next);
×
209
                    }
210

211
                    $callable = $instance ?? $resolved;
×
212
                    if ($instance && $method) {
×
213
                        $callable = [$instance, $method];
×
214
                    }
215

216
                    if ($this->container && $callable instanceof Closure) {
×
217
                        $callable = $callable->bindTo($this->container);
×
218
                    }
219
                }
220

221
                if (!is_callable($callable)) {
×
222
                    throw new RuntimeException(
×
223
                        sprintf(
×
224
                            'Middleware %s is not resolvable',
×
225
                            $this->middleware
×
226
                        )
×
227
                    );
×
228
                }
229

230
                return $callable($request, $this->next);
×
231
            }
232
        };
×
233

234
        return $this;
×
235
    }
236

237
    /**
238
     * Add a (non-standard) callable middleware to the stack
239
     *
240
     * Middleware are organized as a stack. That means middleware
241
     * that have been added before will be executed after the newly
242
     * added one (last in, first out).
243
     * @return MiddlewareDispatcher<TContainerInterface>
244
     */
245
    public function addCallable(callable $middleware): self
×
246
    {
247
        $next = $this->tip;
×
248

249
        if ($this->container && $middleware instanceof Closure) {
×
250
            /** @var Closure $middleware */
251
            $middleware = $middleware->bindTo($this->container);
×
252
        }
253

254
        $this->tip = new class ($middleware, $next) implements RequestHandlerInterface {
×
255
            /**
256
             * @var callable
257
             */
258
            private $middleware;
259

260
            /**
261
             * @var RequestHandlerInterface
262
             */
263
            private $next;
264

265
            public function __construct(callable $middleware, RequestHandlerInterface $next)
266
            {
267
                $this->middleware = $middleware;
×
268
                $this->next = $next;
×
269
            }
270

271
            public function handle(ServerRequestInterface $request): ResponseInterface
272
            {
273
                return ($this->middleware)($request, $this->next);
×
274
            }
275
        };
×
276

277
        return $this;
×
278
    }
279
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc