Passed
Pull Request — master (#134)
by Rustam
02:58
created

Group::middleware()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 8
ccs 5
cts 6
cp 0.8333
crap 2.0185
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Router;
6
7
use InvalidArgumentException;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Psr\Http\Server\RequestHandlerInterface;
10
use RuntimeException;
11
use Yiisoft\Http\Method;
12
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;
13
14
use function get_class;
15
use function in_array;
16
use function is_object;
17
18
final class Group implements GroupInterface
19
{
20
    /**
21
     * @var Group[]|Route[]
22
     */
23
    private array $items = [];
24
    private ?string $prefix;
25
    private array $middlewareDefinitions = [];
26
    private ?string $host = null;
27
    private ?string $namePrefix = null;
28
    private bool $routesAdded = false;
29
    private bool $middlewareAdded = false;
30
    private array $disabledMiddlewareDefinitions = [];
31
    private ?MiddlewareDispatcher $dispatcher;
32
33 24
    private function __construct(?string $prefix = null, MiddlewareDispatcher $dispatcher = null)
34
    {
35 24
        $this->dispatcher = $dispatcher;
36 24
        $this->prefix = $prefix;
37 24
    }
38
39
    /**
40
     * Create a new group instance.
41
     *
42
     * @param string|null $prefix URL prefix to prepend to all routes of the group.
43
     * @param MiddlewareDispatcher|null $dispatcher Middleware dispatcher to use for the group.
44
     *
45
     * @return GroupInterface
46
     */
47 24
    public static function create(
48
        ?string $prefix = null,
49
        MiddlewareDispatcher $dispatcher = null
50
    ): GroupInterface {
51 24
        return new self($prefix, $dispatcher);
52
    }
53
54 21
    public function routes(...$routes): GroupInterface
55
    {
56 21
        if ($this->middlewareAdded) {
57 1
            throw new RuntimeException('routes() can not be used after prependMiddleware().');
58
        }
59 20
        $new = clone $this;
60 20
        foreach ($routes as $route) {
61 20
            if ($route instanceof Route || $route instanceof self) {
62 19
                if (!$route->hasDispatcher() && $new->hasDispatcher()) {
63 5
                    $route = $route->withDispatcher($new->dispatcher);
64
                }
65 19
                $new->items[] = $route;
66
            } else {
67 1
                $type = is_object($route) ? get_class($route) : gettype($route);
68 1
                throw new InvalidArgumentException(
69 1
                    sprintf('Route should be either an instance of Route or Group, %s given.', $type)
70
                );
71
            }
72
        }
73
74 19
        $new->routesAdded = true;
75
76 19
        return $new;
77
    }
78
79 2
    public function withDispatcher(MiddlewareDispatcher $dispatcher): GroupInterface
80
    {
81 2
        $group = clone $this;
82 2
        $group->dispatcher = $dispatcher;
83 2
        foreach ($group->items as $index => $item) {
84 2
            if (!$item->hasDispatcher()) {
85 2
                $item = $item->withDispatcher($dispatcher);
86 2
                $group->items[$index] = $item;
87
            }
88
        }
89
90 2
        return $group;
91
    }
92
93 3
    public function withAutoOptions(...$middlewares): GroupInterface
94
    {
95 3
        if (!$this->routesAdded) {
96
            throw new RuntimeException('withAutoOptions() can not be used before routes().');
97
        }
98
99 3
        if (empty($middlewares)) {
100
            throw new InvalidArgumentException('At least one middleware must be specified.');
101
        }
102
103 3
        $group = clone $this;
104 3
        $pattern = null;
105 3
        $host = null;
106 3
        foreach ($group->items as $index => $item) {
107 3
            if ($item instanceof self) {
108 1
                $item = $item->withAutoOptions(...$middlewares);
109 1
                $group->items[$index] = $item;
110
            } else {
111
                // Avoid duplicates
112
                if (
113 3
                    ($pattern === $item->getPattern() && $host === $item->getHost())
114 3
                    || in_array(Method::OPTIONS, $item->getMethods(), true)
115
                ) {
116 3
                    continue;
117
                }
118 3
                $pattern = $item->getPattern();
119 3
                $host = $item->getHost();
120 3
                $route = Route::options($pattern);
121 3
                if ($host !== null) {
122 1
                    $route = $route->host($host);
123
                }
124 3
                foreach ($middlewares as $middleware) {
125 3
                    $route = $route->middleware($middleware);
126 3
                    $item = $item->prependMiddleware($middleware);
127
                }
128 3
                $group->items[$index] = $item;
129 3
                $group->items[] = $route->action(
130 3
                    static fn (ServerRequestInterface $request, RequestHandlerInterface $handler) => $handler->handle(
131
                        $request
132
                    )
133 3
                );
134
            }
135
        }
136
137 3
        return $group;
138
    }
139
140 19
    public function hasDispatcher(): bool
141
    {
142 19
        return $this->dispatcher !== null;
143
    }
144
145 7
    public function middleware($middlewareDefinition): GroupInterface
146
    {
147 7
        if ($this->routesAdded) {
148
            throw new RuntimeException('middleware() can not be used after routes().');
149
        }
150 7
        $new = clone $this;
151 7
        array_unshift($new->middlewareDefinitions, $middlewareDefinition);
152 7
        return $new;
153
    }
154
155 3
    public function prependMiddleware($middlewareDefinition): GroupInterface
156
    {
157 3
        $new = clone $this;
158 3
        $new->middlewareDefinitions[] = $middlewareDefinition;
159 3
        $new->middlewareAdded = true;
160 3
        return $new;
161
    }
162
163 3
    public function namePrefix(string $namePrefix): GroupInterface
164
    {
165 3
        $new = clone $this;
166 3
        $new->namePrefix = $namePrefix;
167 3
        return $new;
168
    }
169
170 2
    public function host(string $host): GroupInterface
171
    {
172 2
        $new = clone $this;
173 2
        $new->host = rtrim($host, '/');
174 2
        return $new;
175
    }
176
177
    public function disableMiddleware($middlewareDefinition): GroupInterface
178
    {
179
        $new = clone $this;
180
        $new->disabledMiddlewareDefinitions[] = $middlewareDefinition;
181
        return $new;
182
    }
183
184
    /**
185
     * @return Group[]|Route[]
186
     */
187 18
    public function getItems(): array
188
    {
189 18
        return $this->items;
190
    }
191
192 17
    public function getPrefix(): ?string
193
    {
194 17
        return $this->prefix;
195
    }
196
197 17
    public function getNamePrefix(): ?string
198
    {
199 17
        return $this->namePrefix;
200
    }
201
202 17
    public function getHost(): ?string
203
    {
204 17
        return $this->host;
205
    }
206
207 14
    public function getMiddlewareDefinitions(): array
208
    {
209 14
        foreach ($this->middlewareDefinitions as $index => $definition) {
210 8
            if (in_array($definition, $this->disabledMiddlewareDefinitions, true)) {
211
                unset($this->middlewareDefinitions[$index]);
212
            }
213
        }
214
215 14
        return $this->middlewareDefinitions;
216
    }
217
}
218