Passed
Push — master ( 1da3d9...4f22f8 )
by Alexander
23:29 queued 21:59
created

RouteCollection::getRoute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 8
ccs 4
cts 5
cp 0.8
crap 2.032
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Router;
6
7
use InvalidArgumentException;
8
9
final class RouteCollection implements RouteCollectionInterface
10
{
11
    private RouteCollectorInterface $collector;
12
13
    private array $items = [];
14
15
    /**
16
     * All attached routes as Route instances
17
     *
18
     * @var Route[]
19
     */
20
    private array $routes = [];
21
22 4
    public function __construct(RouteCollectorInterface $collector)
23
    {
24 4
        if ($collector instanceof Group && count($collector->getMiddlewares()) > 0) {
25
            throw new InvalidArgumentException('Collector can\'t have middlewares');
26
        }
27 4
        $this->collector = $collector;
28 4
    }
29
30
    /**
31
     * @return Route[]
32
     */
33 1
    public function getRoutes(): array
34
    {
35 1
        $this->ensureItemsInjected();
36
        return $this->routes;
37
    }
38
39
    /**
40
     * @param string $name
41
     * @return Route
42
     */
43 3
    public function getRoute(string $name): Route
44
    {
45 3
        $this->ensureItemsInjected();
46 3
        if (!array_key_exists($name, $this->routes)) {
47
            throw new RouteNotFoundException($name);
48
        }
49
50 3
        return $this->routes[$name];
51
    }
52
53
    /**
54
     * Returns routes tree array
55
     *
56
     * @param bool $routeAsString
57
     * @return array
58
     */
59
    public function getRouteTree(bool $routeAsString = true): array
60
    {
61
        $this->ensureItemsInjected();
62
        return $this->buildTree($this->items, $routeAsString);
63
    }
64
65 4
    private function ensureItemsInjected(): void
66
    {
67 4
        if ($this->items === []) {
68 4
            $this->injectItems($this->collector->getItems());
69
        }
70 3
    }
71
72
    /**
73
     * Build routes array
74
     *
75
     * @param Route[]|Group[] $items
76
     */
77 4
    private function injectItems(array $items): void
78
    {
79 4
        foreach ($items as $index => $item) {
80 4
            $this->injectItem($item);
81
        }
82 3
    }
83
84
    /**
85
     * Add an item into routes array
86
     * @param Route|Group $route
87
     */
88 4
    private function injectItem($route): void
89
    {
90 4
        if ($route instanceof Group) {
91 3
            $this->injectGroup($route, $this->items);
92 3
            return;
93
        }
94
95 1
        $this->items[] = $route->getName();
96 1
        $routeName = $route->getName();
97 1
        if (isset($this->routes[$routeName])) {
98 1
            throw new InvalidArgumentException("A route with name '$routeName' already exists.");
99
        }
100 1
        $this->routes[$routeName] = $route;
101 1
    }
102
103
    /**
104
     * Inject a Group instance into route and item arrays.
105
     */
106 3
    private function injectGroup(Group $group, array &$tree, string $prefix = ''): void
107
    {
108 3
        $prefix .= $group->getPrefix();
109
        /** @var $items Group[]|Route[] */
110 3
        $items = $group->getItems();
111 3
        foreach ($items as $index => $item) {
112 3
            $groupMiddlewares = $group->getMiddlewares();
113 3
            foreach ($groupMiddlewares as $middleware) {
114 3
                $item = $item->addMiddleware($middleware);
115
            }
116
117 3
            if ($item instanceof Group) {
118 1
                if (empty($item->getPrefix())) {
119
                    $this->injectGroup($item, $tree, $prefix);
120
                    continue;
121
                }
122 1
                $tree[$item->getPrefix()] = [];
123 1
                $this->injectGroup($item, $tree[$item->getPrefix()], $prefix);
124 1
                continue;
125
            }
126
127
            /** @var Route $modifiedItem */
128 3
            $modifiedItem = $item->pattern($prefix . $item->getPattern());
129
130 3
            if (empty($tree[$group->getPrefix()])) {
131 3
                $tree[] = $modifiedItem->getName();
132
            } else {
133
                $tree[$group->getPrefix()][] = $modifiedItem->getName();
134
            }
135
136 3
            $routeName = $modifiedItem->getName();
137 3
            if (isset($this->routes[$routeName])) {
138
                throw new InvalidArgumentException("A route with name '$routeName' already exists.");
139
            }
140 3
            $this->routes[$routeName] = $modifiedItem;
141
        }
142 3
    }
143
144
    /**
145
     * Builds route tree from items
146
     *
147
     * @param array $items
148
     * @param bool $routeAsString
149
     * @return array
150
     */
151
    private function buildTree(array $items, bool $routeAsString): array
152
    {
153
        $tree = [];
154
        foreach ($items as $key => $item) {
155
            if (is_array($item)) {
156
                $tree[$key] = $this->buildTree($items[$key], $routeAsString);
157
            } else {
158
                $tree[] = $routeAsString ? (string)$this->getRoute($item) : $this->getRoute($item);
159
            }
160
        }
161
        return $tree;
162
    }
163
}
164