RouteCollection::getRoutes()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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