Passed
Push — master ( f8dde9...fe9361 )
by Alexander
10:36
created

Group::withDispatcher()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

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