Passed
Pull Request — master (#194)
by Alexander
02:33
created

Group::create()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Router;
6
7
use InvalidArgumentException;
8
use RuntimeException;
9
10
use function in_array;
11
12
final class Group
13
{
14
    /**
15
     * @var Group[]|Route[]
16
     */
17
    private array $items = [];
18
19
    /**
20
     * @var array[]|callable[]|string[]
21
     */
22
    private array $middlewareDefinitions = [];
23
24
    /**
25
     * @var string[]
26
     */
27
    private array $hosts = [];
28
    private ?string $namePrefix = null;
29
    private bool $routesAdded = false;
30
    private bool $middlewareAdded = false;
31
    private array $disabledMiddlewareDefinitions = [];
32
33
    /**
34
     * @var array|callable|string|null Middleware definition for CORS requests.
35
     */
36
    private $corsMiddleware = null;
37
38 32
    private function __construct(private ?string $prefix = null)
39
    {
40 32
    }
41
42
    /**
43
     * Create a new group instance.
44
     *
45
     * @param string|null $prefix URL prefix to prepend to all routes of the group.
46
     */
47 32
    public static function create(
48
        ?string $prefix = null,
49
    ): self {
50 32
        return new self($prefix);
51
    }
52
53 24
    public function routes(self|Route ...$routes): self
54
    {
55 24
        if ($this->middlewareAdded) {
56 1
            throw new RuntimeException('routes() can not be used after prependMiddleware().');
57
        }
58 23
        $new = clone $this;
59 23
        $new->items = $routes;
60
61 23
        $new->routesAdded = true;
62
63 23
        return $new;
64
    }
65
66
    /**
67
     * Adds a middleware definition that handles CORS requests.
68
     * If set, routes for {@see Method::OPTIONS} request will be added automatically.
69
     *
70
     * @param array|callable|string|null $middlewareDefinition Middleware definition for CORS requests.
71
     */
72 6
    public function withCors(array|callable|string|null $middlewareDefinition): self
73
    {
74 6
        $group = clone $this;
75 6
        $group->corsMiddleware = $middlewareDefinition;
76
77 6
        return $group;
78
    }
79
80
    /**
81
     * Appends a handler middleware definition that should be invoked for a matched route.
82
     * First added handler will be executed first.
83
     */
84 11
    public function middleware(array|callable|string ...$middlewareDefinition): self
85
    {
86 11
        if ($this->routesAdded) {
87 1
            throw new RuntimeException('middleware() can not be used after routes().');
88
        }
89 10
        $new = clone $this;
90 10
        array_push(
91 10
            $new->middlewareDefinitions,
92 10
            ...array_values($middlewareDefinition)
93 10
        );
94 10
        return $new;
95
    }
96
97
    /**
98
     * Prepends a handler middleware definition that should be invoked for a matched route.
99
     * First added handler will be executed last.
100
     */
101 23
    public function prependMiddleware(array|callable|string ...$middlewareDefinition): self
102
    {
103 23
        $new = clone $this;
104 23
        array_unshift(
105 23
            $new->middlewareDefinitions,
106 23
            ...array_values($middlewareDefinition)
107 23
        );
108 23
        $new->middlewareAdded = true;
109 23
        return $new;
110
    }
111
112 4
    public function namePrefix(string $namePrefix): self
113
    {
114 4
        $new = clone $this;
115 4
        $new->namePrefix = $namePrefix;
116 4
        return $new;
117
    }
118
119 2
    public function host(string $host): self
120
    {
121 2
        return $this->hosts($host);
122
    }
123
124 5
    public function hosts(string ...$hosts): self
125
    {
126 5
        $new = clone $this;
127
128 5
        foreach ($hosts as $host) {
129 4
            $host = rtrim($host, '/');
130
131 4
            if ($host !== '' && !in_array($host, $new->hosts, true)) {
132 4
                $new->hosts[] = $host;
133
            }
134
        }
135
136 5
        return $new;
137
    }
138
139
    /**
140
     * Excludes middleware from being invoked when action is handled.
141
     * It is useful to avoid invoking one of the parent group middleware for
142
     * a certain route.
143
     */
144 3
    public function disableMiddleware(mixed ...$middlewareDefinition): self
145
    {
146 3
        $new = clone $this;
147 3
        array_push(
148 3
            $new->disabledMiddlewareDefinitions,
149 3
            ...array_values($middlewareDefinition),
150 3
        );
151 3
        return $new;
152
    }
153
154
    /**
155
     * @psalm-template T as string
156
     *
157
     * @psalm-param T $key
158
     *
159
     * @psalm-return (
160
     *   T is ('prefix'|'namePrefix'|'host') ? string|null :
161
     *   (T is 'items' ? Group[]|Route[] :
162
     *     (T is 'hosts' ? array<array-key, string> :
163
     *       (T is ('hasCorsMiddleware'') ? bool :
164
     *         (T is 'middlewareDefinitions' ? list<array|callable|string> :
165
     *           (T is 'corsMiddleware' ? array|callable|string|null : mixed)
166
     *         )
167
     *       )
168
     *     )
169
     *   )
170
     * )
171
     */
172 28
    public function getData(string $key): mixed
173
    {
174 28
        return match ($key) {
175 28
            'prefix' => $this->prefix,
176 28
            'namePrefix' => $this->namePrefix,
177 28
            'host' => $this->hosts[0] ?? null,
178 28
            'hosts' => $this->hosts,
179 28
            'corsMiddleware' => $this->corsMiddleware,
180 28
            'items' => $this->items,
181 28
            'hasCorsMiddleware' => $this->corsMiddleware !== null,
182 28
            'middlewareDefinitions' => $this->getMiddlewareDefinitions(),
183 28
            default => throw new InvalidArgumentException('Unknown data key: ' . $key),
184 28
        };
185
    }
186
187 19
    private function getMiddlewareDefinitions(): array
188
    {
189
        /** @var mixed $definition */
190 19
        foreach ($this->middlewareDefinitions as $index => $definition) {
191 11
            if (in_array($definition, $this->disabledMiddlewareDefinitions, true)) {
192 2
                unset($this->middlewareDefinitions[$index]);
193
            }
194
        }
195
196 19
        return array_values($this->middlewareDefinitions);
197
    }
198
}
199