Passed
Push — master ( a7de24...4420d5 )
by Alexander
02:32 queued 10s
created

Group::addMiddleware()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 6
rs 10
ccs 4
cts 4
cp 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Router;
6
7
use InvalidArgumentException;
8
use Psr\Http\Server\MiddlewareInterface;
9
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;
10
11
final class Group implements RouteCollectorInterface
12
{
13
    /**
14
     * @var Group[]|Route[]
15
     */
16
    protected array $items = [];
17
    protected ?string $prefix;
18
    protected array $middlewares = [];
19
    private ?MiddlewareDispatcher $dispatcher = null;
20
21 9
    private function __construct(?string $prefix = null, ?callable $callback = null, MiddlewareDispatcher $dispatcher = null)
22
    {
23 9
        $this->dispatcher = $dispatcher;
24 9
        $this->prefix = $prefix;
25
26 9
        if ($callback !== null) {
27 9
            $callback($this);
28
        }
29 9
    }
30
31
    /**
32
     * Create a new instance
33
     *
34
     * @param string|null $prefix
35
     * @param callable|array $routes
36
     * @param MiddlewareDispatcher $dispatcher
37
     *
38
     * @return self
39
     */
40 9
    public static function create(?string $prefix = null, $routes = [], MiddlewareDispatcher $dispatcher = null): self
41
    {
42 9
        if (\is_callable($routes)) {
43 4
            $callback = $routes;
44 8
        } elseif (is_array($routes)) {
45 8
            $callback = static function (Group $group) use (&$routes) {
46 8
                foreach ($routes as $route) {
47 2
                    if ($route instanceof Route) {
48 2
                        $group->addRoute($route);
49 2
                    } elseif ($route instanceof Group) {
50 2
                        $group->addGroup($route);
51
                    } else {
52
                        throw new InvalidArgumentException('Route should be either instance of Route or Group.');
53
                    }
54
                }
55 8
            };
56
        } else {
57
            $callback = null;
58
        }
59
60 9
        return new self($prefix, $callback, $dispatcher);
61
    }
62
63 2
    public function withDispatcher(MiddlewareDispatcher $dispatcher): self
64
    {
65 2
        $group = clone $this;
66 2
        $group->dispatcher = $dispatcher;
67 2
        foreach ($group->items as $index => $item) {
68 2
            if (!$item->hasDispatcher()) {
69 2
                $item = $item->withDispatcher($dispatcher);
70 2
                $group->items[$index] = $item;
71
            }
72
        }
73
74 2
        return $group;
75
    }
76
77 8
    public function hasDispatcher(): bool
78
    {
79 8
        return $this->dispatcher !== null;
80
    }
81
82 8
    public function addRoute(Route $route): self
83
    {
84 8
        if (!$route->hasDispatcher() && $this->hasDispatcher()) {
85 3
            $route->injectDispatcher($this->dispatcher);
86
        }
87 8
        $this->items[] = $route;
88 8
        return $this;
89
    }
90
91 6
    public function addGroup(Group $group): self
92
    {
93 6
        if (!$group->hasDispatcher() && $this->hasDispatcher()) {
94 2
            $group = $group->withDispatcher($this->dispatcher);
95
        }
96 6
        $this->items[] = $group;
97 6
        return $this;
98
    }
99
100
    /**
101
     * @param MiddlewareInterface|callable|string|array $middleware
102
     */
103 6
    private function validateMiddleware($middleware): void
104
    {
105
        if (
106 6
            is_string($middleware) && is_subclass_of($middleware, MiddlewareInterface::class)
107
        ) {
108
            return;
109
        }
110
111 6
        if ($this->isCallable($middleware) && (!is_array($middleware) || !is_object($middleware[0]))) {
112 6
            return;
113
        }
114
115
        if (is_scalar($middleware)) {
116
            $type = gettype($middleware) . ' with value "' . $middleware . '"';
117
        } elseif (is_object($middleware)) {
118
            $type = 'an instance of ' . get_class($middleware);
119
120
        } else {
121
            $type = gettype($middleware);
122
        }
123
124
        throw new InvalidArgumentException("Parameter should be either PSR middleware class name or a callable, $type given.");
125
    }
126
127 6
    private function isCallable($definition): bool
128
    {
129 6
        if (is_callable($definition)) {
130 6
            return true;
131
        }
132
133
        return is_array($definition) && array_keys($definition) === [0, 1] && in_array($definition[1], get_class_methods($definition[0]) ?? [], true);
134
    }
135
136
    /**
137
     * @param callable|MiddlewareInterface $middleware
138
     * @return $this
139
     */
140 6
    public function addMiddleware($middleware): self
141
    {
142 6
        $this->validateMiddleware($middleware);
143 6
        $this->middlewares[] = $middleware;
144
145 6
        return $this;
146
    }
147
148
    /**
149
     * @return Route[]|Group[]
150
     */
151 8
    public function getItems(): array
152
    {
153 8
        return $this->items;
154
    }
155
156 5
    public function getPrefix(): ?string
157
    {
158 5
        return $this->prefix;
159
    }
160
161 7
    public function getMiddlewares(): array
162
    {
163 7
        return $this->middlewares;
164
    }
165
}
166