Test Failed
Push — master ( d4b731...813def )
by Dan
08:06
created

RouteCollection   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 305
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 94.12%

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 3
dl 0
loc 305
ccs 80
cts 85
cp 0.9412
rs 9
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getRoutes() 0 4 1
A rewind() 0 4 1
A addNamespace() 0 5 1
A addGlobalNames() 0 6 2
A group() 0 10 1
A addRoute() 0 20 3
B _findHandlerNamespace() 0 16 5
A _isUnique() 0 12 4
A _getRouteGlobals() 0 8 1
A _mergeGroupNames() 0 10 2
A formatRoutePattern() 0 17 4
A mergeCollection() 0 21 4
A current() 0 4 1
A next() 0 4 1
A key() 0 4 1
A valid() 0 5 2
A count() 0 4 1
1
<?php
2
/**
3
 * This file is part of the RS Framework.
4
 *
5
 * (c) Dan Smith <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace Ds\Router;
11
12
use Ds\Router\Exceptions\RouteException;
13
use Ds\Router\Exceptions\UniqueRouteException;
14
use Ds\Router\Interfaces\RouteCollectionInterface;
15
use Ds\Router\Interfaces\RouteInterface;
16
17
/**
18
 * Class RouteCollection
19
 *
20
 * @package Rs\Router
21
 * @author  Dan Smith    <[email protected]>
22
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
23
 * @link    https://red-sqr.co.uk/Framework/Router/wikis/
24
 *
25
 * @see FileLoader
26
 * @see YamlLoader
27
 * @see RouteInterface
28
 * @see RouterInterface
29
 */
30
class RouteCollection implements RouteCollectionInterface
31
{
32
    /**
33
     *
34
     * Controller::Method delimiter.
35
     *
36
     * @var string
37
     */
38
    protected $delimiter = '::';
39
40
    /**
41
     * RouteInterfaces
42
     *
43
     * @var array.
44
     */
45
    protected $collection = [];
46
    /**
47
     * Route group context.
48
     *
49
     * @var array
50
     */
51
    protected $group = [];
52
    /**
53
     * Route group names.
54
     *
55
     * @var array
56
     */
57
    protected $groupNames = [];
58
    /**
59
     * Namespaces for Controller::Method handler.
60
     *
61
     * @var array
62
     */
63
    protected $namespace = [];
64
    /**
65
     * Global Collection route names.
66
     *
67
     * @var array
68
     */
69
    protected $globalRouteNames = [];
70
    /**
71
     * Is grouped callback flag.
72
     *
73
     * @var bool
74
     */
75
    private $_isGrouped = false;
76
77
    /**
78
     * Add Namespace to Route Collection.
79
     *
80
     * @param string $namespace Controller Namespace.
81
     */
82 2
    public function addNamespace(string $namespace)
83
    {
84 2
        $formattedNamespace = '\\'.ltrim($namespace,'\\');
85 2
        $this->namespace[] = $formattedNamespace;
86 2
    }
87
88
    /**
89
     * @param array $names
90
     */
91
    public function addGlobalNames(array $names)
92
    {
93
        foreach ($names as $name) {
94
            $this->globalRouteNames[$name] = $name;
95
        }
96
    }
97
98
    /**
99
     * @inheritdoc
100
     */
101 2
    public function group($path = '', callable $routeCallback, array $names = [])
102
    {
103 2
        $this->group[] = $path;
104 2
        $this->groupNames[] = $names;
105 2
        $this->_isGrouped = true;
106 2
        $routeCallback();
107 2
        $this->_isGrouped = false;
108 2
        \array_pop($this->group);
109 2
        \array_pop($this->groupNames);
110 2
    }
111
112
    /**
113
     * @inheritdoc
114
     *
115
     * @throws \Rs\Router\Exceptions\UniqueRouteException
116
     */
117 19
    public function addRoute($method, $pattern, $handler, $name = [])
118
    {
119 19
        $routePattern = $this->formatRoutePattern($pattern);
120 19
        $this->_isUnique($method, $routePattern);
121 19
        $routeNames = $this->_getRouteGlobals($this->_mergeGroupNames($name));
122
123 19
        if (\is_string($handler)) {
124 16
            $handler = $this->_findHandlerNamespace($handler);
125
        }
126
127 19
        foreach ((array)$method as $httpMethod) {
128 19
            $this->collection[] = new Route(
129
                $httpMethod,
130
                $routePattern,
131
                $handler,
132
                $routeNames
133
            );
134
        }
135 19
        return $routePattern;
136
    }
137
138
    /**
139
     * Add Namespace to handler.
140
     *
141
     * @param mixed $handler
142
     *
143
     * @return mixed
144
     */
145 18
    private function _findHandlerNamespace($handler)
146
    {
147 18
        $handler = \str_replace("/", "\\", $handler);
148
149 18
        if (\is_string($handler)) {
150 18
            $p = \explode($this->delimiter, $handler);
151 18
            if (isset($p[1])) {
152 6
                foreach ($this->namespace as $ns) {
153 2
                    if (\class_exists($ns . '\\' . $p[0])) {
154 2
                        $handler = $ns . '\\' . $p[0] . $this->delimiter . $p[1];
155
                    }
156
                }
157
            }
158
        }
159 18
        return $handler;
160
    }
161
162
    /**
163
     * Check that the Route is unique before adding it (again).
164
     *
165
     * @param  $method
166
     * @param  $pattern
167
     * @return bool
168
     * @throws UniqueRouteException
169
     */
170 19
    private function _isUnique($method, $pattern)
171
    {
172
        /**
173
         * @var Route $route
174
         */
175 19
        foreach ($this->collection as $route) {
176 11
            if ($route->getMethod() === $method && $route->getPattern() === $pattern) {
177 11
                throw new UniqueRouteException('Route has already been added.');
178
            }
179
        }
180 19
        return true;
181
    }
182
183
    /**
184
     * Get Collection global route names.
185
     *
186
     * @param array $names Routes names to merge with globals.
187
     *
188
     * @return array
189
     */
190 19
    private function _getRouteGlobals(array $names)
191
    {
192 19
        return \array_unique(
193
            \array_merge(
194 19
                \array_values($this->globalRouteNames), $names
195 19
            ), SORT_REGULAR
196
        );
197
    }
198
199
    /**
200
     * Merge Route Names with group.
201
     *
202
     * @param array $names Route names to be added to group.
203
     *
204
     * @return array
205
     */
206 19
    private function _mergeGroupNames(array $names)
207
    {
208 19
        $finalStack = [];
209
210 19
        if (\count($this->groupNames) !== 0) {
211 2
            $finalStack = \array_merge(...$this->groupNames);
212
        }
213
214 19
        return \array_unique(\array_merge($names, $finalStack));
215
    }
216
217
    /**
218
     * Format Route Pattern and return with grouped directory is necessary.
219
     *
220
     * @param  $pattern
221
     * @return string
222
     */
223 20
    public function formatRoutePattern($pattern)
224
    {
225 20
        $routePattern = '/' . \ltrim($pattern, '/');
226
227 20
        if ($this->_isGrouped === false) {
228 18
            return $routePattern;
229
        }
230
231 2
        $route = \implode('', $this->group) . $routePattern;
232
233 2
        if (substr($route, -1) === '/' && strlen($route) > 1){
234
            $route = rtrim($route,'/');
235
        }
236
237 2
        return $route;
238
239
    }
240
241
    /**
242
     * @inheritdoc
243
     */
244 2
    public function mergeCollection(RouteCollectionInterface $collection, bool $throw = true)
245
    {
246 2
        $new = clone $this;
247 2
        foreach ($collection as $addRoute) {
248
            try {
249 2
                $new->_isUnique(
250 2
                    $addRoute->getMethod(),
251 2
                    $this->formatRoutePattern(
252 2
                        $addRoute->getPattern()
253
                    )
254
                );
255 1
                $new->collection[] = $addRoute;
256 1
            } catch (\Exception $e) {
257 1
                if ($throw) {
258 1
                    throw new RouteException($e->getMessage());
259
                }
260 1
                continue;
261
            }
262
        }
263 1
        return $new;
264
    }
265
266
    /**
267
     * @inheritdoc
268
     */
269 1
    public function getRoutes()
270
    {
271 1
        return $this->collection;
272
    }
273
274
    /**
275
     * Returns current route.
276
     *
277
     * @return RouteInterface
278
     */
279 8
    public function current()
280
    {
281 8
        return \current($this->collection);
282
    }
283
284
    /**
285
     * Returns next route.
286
     *
287
     * @return RouteInterface
288
     */
289 5
    public function next()
290
    {
291 5
        return \next($this->collection);
292
    }
293
294
    /**
295
     * Returns current route index.
296
     *
297
     * @return mixed
298
     */
299 3
    public function key()
300
    {
301 3
        return \key($this->collection);
302
    }
303
304
    /**
305
     * If last route not reached.
306
     *
307
     * @return bool
308
     */
309 5
    public function valid()
310
    {
311 5
        $key = \key($this->collection);
312 5
        return ($key !== null && $key !== false);
313
    }
314
315
    /**
316
     * Return to and return first route.
317
     *
318
     * @return RouteInterface
319
     */
320 4
    public function rewind()
321
    {
322 4
        return \reset($this->collection);
323
    }
324
325
    /**
326
     * Get total routes in collection.
327
     *
328
     * @return int
329
     */
330 2
    public function count()
331
    {
332 2
        return \count($this->collection);
333
    }
334
}
335