Passed
Pull Request — master (#143)
by Sergei
03:01
created

Group::withDispatcher()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

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