Test Failed
Push — master ( 0ee32e...41c938 )
by Dan
07:10
created

RouteCollection   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 306
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 3
dl 0
loc 306
rs 9
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A addNamespace() 0 4 1
A addGlobalNames() 0 6 2
A group() 0 10 1
A addRoute() 0 22 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 getRoutes() 0 4 1
A current() 0 4 1
A next() 0 4 1
A key() 0 4 1
A valid() 0 5 2
A rewind() 0 4 1
A count() 0 4 1
1
<?php
2
/**
3
 * This file is part of the PSR Http 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://github.com/djsmithme/Router
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
    public function addNamespace(string $namespace)
83
    {
84
        $this->namespace[] = $namespace;
85
    }
86
87
    /**
88
     * @param array $names
89
     */
90
    public function addGlobalNames(array $names)
91
    {
92
        foreach ($names as $name) {
93
            $this->globalRouteNames[$name] = $name;
94
        }
95
    }
96
97
    /**
98
     * @inheritdoc
99
     */
100
    public function group($path = '', callable $routeCallback, array $names = [])
101
    {
102
        $this->group[] = $path;
103
        $this->groupNames[] = $names;
104
        $this->_isGrouped = true;
105
        $routeCallback();
106
        $this->_isGrouped = false;
107
        \array_pop($this->group);
108
        \array_pop($this->groupNames);
109
    }
110
111
    /**
112
     * @inheritdoc
113
     *
114
     * @throws \Rs\Router\Exceptions\UniqueRouteException
115
     */
116
    public function addRoute($method, $pattern, $handler, $name = [])
117
    {
118
        if (\is_string($handler)) {
119
            $handler = $this->_findHandlerNamespace($handler);
120
        }
121
122
        $this->_isUnique($method, $pattern);
123
124
        $routeNames = $this->_getRouteGlobals($this->_mergeGroupNames($name));
125
126
        $routePattern = $this->formatRoutePattern($pattern);
127
128
        foreach ((array)$method as $httpMethod) {
129
            $this->collection[] = new Route(
130
                $httpMethod,
131
                $routePattern,
132
                $handler,
133
                $routeNames
134
            );
135
        }
136
        return $routePattern;
137
    }
138
139
    /**
140
     * Add Namespace to handler.
141
     *
142
     * @param mixed $handler
143
     *
144
     * @return mixed
145
     */
146
    private function _findHandlerNamespace($handler)
147
    {
148
        $handler = \str_replace("/", "\\", $handler);
149
150
        if (\is_string($handler)) {
151
            $p = \explode($this->delimiter, $handler);
152
            if (isset($p[1])) {
153
                foreach ($this->namespace as $ns) {
154
                    if (\class_exists($ns . '\\' . $p[0])) {
155
                        $handler = $ns . '\\' . $p[0] . $this->delimiter . $p[1];
156
                    }
157
                }
158
            }
159
        }
160
        return $handler;
161
    }
162
163
    /**
164
     * Check that the Route is unique before adding it (again).
165
     *
166
     * @param  $method
167
     * @param  $pattern
168
     * @return bool
169
     * @throws UniqueRouteException
170
     */
171
    private function _isUnique($method, $pattern)
172
    {
173
        /**
174
         * @var Route $route
175
         */
176
        foreach ($this->collection as $route) {
177
            if ($route->getMethod() === $method && $route->getPattern() === $pattern) {
178
                throw new UniqueRouteException('Route has already been added.');
179
            }
180
        }
181
        return true;
182
    }
183
184
    /**
185
     * Get Collection global route names.
186
     *
187
     * @param array $names Routes names to merge with globals.
188
     *
189
     * @return array
190
     */
191
    private function _getRouteGlobals(array $names)
192
    {
193
        return \array_unique(
194
            \array_merge(
195
                \array_values($this->globalRouteNames), $names
196
            ), SORT_REGULAR
197
        );
198
    }
199
200
    /**
201
     * Merge Route Names with group.
202
     *
203
     * @param array $names Route names to be added to group.
204
     *
205
     * @return array
206
     */
207
    private function _mergeGroupNames(array $names)
208
    {
209
        $finalStack = [];
210
211
        if (\count($this->groupNames) !== 0) {
212
            $finalStack = \array_merge(...$this->groupNames);
213
        }
214
215
        return \array_unique(\array_merge($names, $finalStack));
216
    }
217
218
    /**
219
     * Format Route Pattern and return with grouped directory is necessary.
220
     *
221
     * @param  $pattern
222
     * @return string
223
     */
224
    public function formatRoutePattern($pattern)
225
    {
226
        $routePattern = '/' . \ltrim($pattern, '/');
227
228
        if ($this->_isGrouped === false) {
229
            return $routePattern;
230
        }
231
232
        $route = \implode('', $this->group) . $routePattern;
233
234
        if (substr($route, -1) === '/' && strlen($route) > 1){
235
            $route = rtrim($route,'/');
236
        }
237
238
        return $route;
239
240
    }
241
242
    /**
243
     * @inheritdoc
244
     */
245
    public function mergeCollection(RouteCollectionInterface $collection, bool $throw = true)
246
    {
247
        $new = clone $this;
248
        foreach ($collection as $addRoute) {
249
            try {
250
                $new->_isUnique(
251
                    $addRoute->getMethod(),
252
                    $this->formatRoutePattern(
253
                        $addRoute->getPattern()
254
                    )
255
                );
256
                $new->collection[] = $addRoute;
257
            } catch (\Exception $e) {
258
                if ($throw) {
259
                    throw new RouteException($e->getMessage());
260
                }
261
                continue;
262
            }
263
        }
264
        return $new;
265
    }
266
267
    /**
268
     * @inheritdoc
269
     */
270
    public function getRoutes()
271
    {
272
        return $this->collection;
273
    }
274
275
    /**
276
     * Returns current route.
277
     *
278
     * @return RouteInterface
279
     */
280
    public function current()
281
    {
282
        return \current($this->collection);
283
    }
284
285
    /**
286
     * Returns next route.
287
     *
288
     * @return RouteInterface
289
     */
290
    public function next()
291
    {
292
        return \next($this->collection);
293
    }
294
295
    /**
296
     * Returns current route index.
297
     *
298
     * @return mixed
299
     */
300
    public function key()
301
    {
302
        return \key($this->collection);
303
    }
304
305
    /**
306
     * If last route not reached.
307
     *
308
     * @return bool
309
     */
310
    public function valid()
311
    {
312
        $key = \key($this->collection);
313
        return ($key !== null && $key !== false);
314
    }
315
316
    /**
317
     * Return to and return first route.
318
     *
319
     * @return RouteInterface
320
     */
321
    public function rewind()
322
    {
323
        return \reset($this->collection);
324
    }
325
326
    /**
327
     * Get total routes in collection.
328
     *
329
     * @return int
330
     */
331
    public function count()
332
    {
333
        return \count($this->collection);
334
    }
335
}
336