Test Failed
Pull Request — master (#16)
by Divine Niiquaye
13:18
created

Router   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Test Coverage

Coverage 93.02%

Importance

Changes 11
Bugs 0 Features 0
Metric Value
eloc 49
dl 0
loc 151
ccs 40
cts 43
cp 0.9302
rs 10
c 11
b 0
f 0
wmc 21

9 Methods

Rating   Name   Duplication   Size   Complexity  
A pipe() 0 4 2
A handle() 0 14 4
A match() 0 9 3
A loadAnnotation() 0 7 3
A getIterator() 0 3 1
A __construct() 0 14 2
A addRoute() 0 12 4
A getProfile() 0 3 1
A setHandlerResolver() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Flight Routing.
7
 *
8
 * PHP version 7.1 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Flight\Routing;
19
20
use Biurad\Annotations\LoaderInterface;
21
use Fig\Http\Message\RequestMethodInterface;
22
use Flight\Routing\Handlers\RouteHandler;
23
use Flight\Routing\Interfaces\RouteCompilerInterface;
24
use Laminas\Stratigility\{MiddlewarePipe, MiddlewarePipeInterface};
25
use Psr\Http\Message\{ResponseFactoryInterface, ResponseInterface, ServerRequestInterface};
26
use Psr\Http\Server\{MiddlewareInterface, RequestHandlerInterface};
27
28
/**
29
 * Aggregate routes for matching and Dispatching.
30
 *
31
 * @author Divine Niiquaye Ibok <[email protected]>
32
 */
33
class Router extends RouteMatcher implements \IteratorAggregate, RequestMethodInterface, RequestHandlerInterface
34
{
35
    /**
36
     * Standard HTTP methods for browser requests.
37
     */
38
    public const HTTP_METHODS_STANDARD = [
39
        self::METHOD_HEAD,
40
        self::METHOD_GET,
41
        self::METHOD_POST,
42
        self::METHOD_PUT,
43
        self::METHOD_PATCH,
44
        self::METHOD_DELETE,
45
        self::METHOD_PURGE,
46
        self::METHOD_OPTIONS,
47
        self::METHOD_TRACE,
48
        self::METHOD_CONNECT,
49
    ];
50
51
    /** @var MiddlewarePipeInterface */
52
    private $pipeline;
53
54
    /** @var ResponseFactoryInterface */
55
    private $responseFactory;
56
57
    /** @var DebugRoute|null */
58
    private $debug;
59 73
60
    /** @var null|callable(mixed,array) */
61
    private $handlerResolver = null;
62
63
    public function __construct(
64
        ResponseFactoryInterface $responseFactory,
65 73
        ?RouteCompilerInterface $compiler = null,
66 73
        ?string $cacheFile = null,
67 73
        bool $debug = false
68
    ) {
69 73
        parent::__construct(new \ArrayIterator(), $compiler, $cacheFile);
70 73
71 73
        // Add Middleware support.
72 73
        $this->pipeline = new MiddlewarePipe();
73
        $this->responseFactory = $responseFactory;
74
75
        // Enable routes profiling ...
76
        $this->debug = $debug ? new DebugRoute() : null;
77
    }
78
79
    /**
80
     * Set the route handler resolver.
81
     *
82
     * @param null|callable(mixed:$handler,array:$arguments) $handlerResolver
83
     */
84
    public function setHandlerResolver(?callable $handlerResolver): void
85
    {
86
        $this->handlerResolver = $handlerResolver;
87
    }
88 73
89
    /**
90 73
     * Adds the given route(s) to the router.
91
     *
92
     * @param Route ...$routes
93
     */
94
    public function addRoute(Route ...$routes): void
95
    {
96
        foreach ($routes as $route) {
97
            if (null === $name = $route->get('name')) {
98
                $route->bind($name = $route->generateRouteName(''));
99
            }
100 73
101
            if (null !== $this->debug) {
102 73
                $this->debug->addProfile($name, $route);
103 73
            }
104 73
105
            $this->routes[] = $route;
106
        }
107
    }
108
109
    /**
110 73
     * Attach middleware to the pipeline.
111
     */
112
    public function pipe(MiddlewareInterface ...$middlewares): void
113
    {
114
        foreach ($middlewares as $middleware) {
115
            $this->pipeline->pipe($middleware);
116 73
        }
117 3
    }
118
119
    /**
120
     * Load routes from annotation.
121 73
     */
122 4
    public function loadAnnotation(LoaderInterface $loader): void
123
    {
124 73
        $annotations = $loader->load();
125
126
        foreach ($annotations as $annotation) {
127
            if ($annotation instanceof RouteCollection) {
128
                $this->addRoute(...$annotation);
129 59
            }
130
        }
131 59
    }
132 3
133
    /**
134
     * {@inheritdoc}
135 56
     *
136
     * @return \ArrayIterator<int,Route>|\ArrayIterator<int,array>
137
     */
138
    public function getIterator(): \ArrayIterator
139
    {
140
        return $this->routes;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->routes returns the type Iterator which includes types incompatible with the type-hinted return ArrayIterator.
Loading history...
141
    }
142
143
    /**
144
     * {@inheritdoc}
145 60
     */
146
    public function match(ServerRequestInterface $request): ?Route
147 60
    {
148 60
        $route = parent::match($request);
149 29
150
        if ($route instanceof Route && null !== $this->debug) {
151
            $this->debug->setMatched($route->get('name'));
152 60
        }
153 1
154 1
        return $route;
155
    }
156
157
    /**
158 60
     * {@inheritdoc}
159
     */
160 60
    public function handle(ServerRequestInterface $request): ResponseInterface
161
    {
162
        $route = $this->match($request);
163
        $handler = new RouteHandler($this->responseFactory, $this->handlerResolver);
164
165
        if (null !== $route && !empty($routeMiddlewares = $route->get('middlewares'))) {
166
            $this->pipe(...$routeMiddlewares);
167 24
        }
168
169 24
        try {
170 2
            return $this->pipeline->process($request->withAttribute(Route::class, $route), $handler);
171
        } finally {
172
            if (null !== $this->debug) {
173 24
                $this->debug->leave();
174 24
            }
175
        }
176
    }
177
178
    /**
179 12
     * Get the profiled routes.
180
     */
181 12
    public function getProfile(): ?DebugRoute
182
    {
183
        return $this->debug;
184
    }
185
}
186