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