Passed
Pull Request — master (#113)
by Alexander
02:46
created

Group::isCallable()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 4.25

Importance

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