Router::addZendRoute()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
/**
3
 * DronePHP (http://www.dronephp.com)
4
 *
5
 * @link      http://github.com/Pleets/DronePHP
6
 * @copyright Copyright (c) 2016-2018 Pleets. (http://www.pleets.org)
7
 * @license   http://www.dronephp.com/license
8
 * @author    Darío Rivera <[email protected]>
9
 */
10
11
namespace Drone\Mvc;
12
13
/**
14
 * Router class
15
 *
16
 * This class build the route and calls to specific application controller
17
 */
18
class Router
19
{
20
    /**
21
     * List of routes
22
     *
23
     * @var array
24
     */
25
    private $routes = [];
26
27
    /**
28
     * The Identifiers builds the route
29
     *
30
     * @var array
31
     */
32
    private $identifiers;
33
34
    /**
35
     * Default identifiers
36
     *
37
     * @var array
38
     */
39
    private $defaults;
40
41
    /**
42
     * Controller instance
43
     *
44
     * @var AbstractController
45
     */
46
    private $controller;
47
48
    /**
49
     * Indicates how the class name could be matched
50
     *
51
     * @var callable
52
     */
53
    private $classNameBuilder;
54
55
    /**
56
     * Zend\Router implementation
57
     *
58
     * @var \Zend\Router\SimpleRouteStack
59
     */
60
    private $zendRouter;
61
62
    /**
63
     * Returns all routes built
64
     *
65
     * @return array
66
     */
67
    public function getRoutes()
68
    {
69
        return $this->routes;
70
    }
71
72
    /**
73
     * Returns all identifiers
74
     *
75
     * @return array
76
     */
77
    public function getIdentifiers()
78
    {
79
        return $this->identifiers;
80
    }
81
82
    /**
83
     * Returns default identifiers
84
     *
85
     * @return array
86
     */
87
    public function getDefaults()
88
    {
89
        return $this->defaults;
90
    }
91
92
    /**
93
     * Returns the controller instance
94
     *
95
     * @throws \RuntimeException
96
     *
97
     * @return AbstractController
98
     */
99 5
    public function getController()
100
    {
101 5
        if (is_null($this->controller)) {
102
            throw new \RuntimeException("No controller matched, try to match first.");
103
        }
104
105 5
        return $this->controller;
106
    }
107
108
    /**
109
     * Returns the class name builder function
110
     *
111
     * @return callable
112
     */
113
    public function getClassNameBuilder()
114
    {
115
        return $this->classNameBuilder;
116
    }
117
118
    /**
119
     * Returns the Zend\Router\SimpleRouteStack object
120
     *
121
     * @return \Zend\Router\SimpleRouteStack
122
     */
123
    public function getZendRouter()
124
    {
125
        return $this->zendRouter;
126
    }
127
128
    /**
129
     * Sets identifiers
130
     *
131
     * @param string $module
132
     * @param string $controller
133
     * @param string $view
134
     *
135
     * @return null
136
     */
137 6
    public function setIdentifiers($module, $controller, $view)
138
    {
139 6
        $identifiers = ["module" => $module, "controller" => $controller, "view" => $view];
140
141 6
        foreach ($identifiers as $key => $identifier) {
142 6
            if (!is_string($identifier)) {
143 6
                throw new \InvalidArgumentException("Invalid type given for '$key'. String expected.");
144
            }
145
        }
146
147 6
        $this->identifiers = [
148 6
            "module"     => $module,
149 6
            "controller" => $controller,
150 6
            "view"       => $view,
151
        ];
152 6
    }
153
154
    /**
155
     * Sets default identifiers
156
     *
157
     * @param string $module
158
     * @param string $controller
159
     * @param string $view
160
     *
161
     * @return null
162
     */
163 1
    public function setDefaults($module, $controller, $view)
164
    {
165 1
        $identifiers = ["module" => $module, "controller" => $controller, "view" => $view];
166
167 1
        foreach ($identifiers as $key => $identifier) {
168 1
            if (!is_string($identifier)) {
169 1
                throw new \InvalidArgumentException("Invalid type given for '$key'. String expected.");
170
            }
171
        }
172
173 1
        $this->defaults = [
174 1
            "module"     => $module,
175 1
            "controller" => $controller,
176 1
            "view"       => $view,
177
        ];
178 1
    }
179
180
    /**
181
     * Sets the class name builder function
182
     *
183
     * @param callable $builder
184
     *
185
     * @return null
186
     */
187 6
    public function setClassNameBuilder(callable $builder)
188
    {
189 6
        $this->classNameBuilder = $builder;
190 6
    }
191
192
    /**
193
     * Constructor
194
     *
195
     * @param array $routes
196
     */
197 6
    public function __construct(array $routes = [])
198
    {
199 6
        if (count($routes)) {
200
            foreach ($routes as $route) {
201
                $this->addRoute($route);
202
            }
203
        }
204
205
        # schema for identifiers
206 6
        $this->identifiers = [
207
            "module"     => '',
208
            "controller" => '',
209
            "view"       => '',
210
        ];
211
212 6
        $this->defaults = [
213
            "module"     => '',
214
            "controller" => '',
215
            "view"       => '',
216
        ];
217
218
        # default class name builder
219
        $this->setClassNameBuilder(function ($module, $class) {
220 2
            return "\\$module\\$class";
221 6
        });
222
223 6
        $this->zendRouter = new \Zend\Router\SimpleRouteStack();
224 6
    }
225
226
    /**
227
     * Builds the current route and calls the controller
228
     *
229
     * @throws Exception\PageNotFoundException
230
     * @throws Exception\RouteNotFoundException
231
     * @throws \LogicException
232
     *
233
     * @return  null
234
     */
235 6
    public function match()
236
    {
237 6
        if (!is_callable($this->classNameBuilder)) {
238
            throw \LogicException("No class name builder found");
0 ignored issues
show
Bug introduced by
The function LogicException was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

238
            throw /** @scrutinizer ignore-call */ \LogicException("No class name builder found");
Loading history...
239
        }
240
241
        /*
242
         *  Key value pairs builder:
243
         *  Searches for the pattern /var1/value1/var2/value2 and converts it to  var1 => value1, var2 => value2
244
         */
245 6
        if (array_key_exists('params', $_GET)) {
246
            $keypairs = $this->parseRequestParameters($_GET["params"]);
0 ignored issues
show
Bug introduced by
The method parseRequestParameters() does not exist on Drone\Mvc\Router. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

246
            /** @scrutinizer ignore-call */ 
247
            $keypairs = $this->parseRequestParameters($_GET["params"]);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
247
            unset($_GET["params"]);
248
            $_GET = array_merge($_GET, $keypairs);
249
        }
250
251
        /*
252
         *  Route builder:
253
         *  The route is built by default from the URL as follow
254
         *  www.example.com/module/controller/view
255
         */
256
257 6
        $match = false;
258
259 6
        if (count($this->routes)) {
260 6
            foreach ($this->routes as $key => $route) {
261 6
                if ($route["module"]     == $this->identifiers["module"]     &&
262 6
                    $route["controller"] == $this->identifiers["controller"] &&
263 6
                    $route["view"]       == $this->identifiers["view"]
264
                ) {
265 5
                    $module     = $route["module"];
266 5
                    $controller = $route["controller"];
267 5
                    $view       = $route["view"];
268
269 5
                    $match = true;
270 6
                    break;
271
                }
272
            }
273
        }
274
275 6
        if (count($this->defaults) && !$match) {
276 2
            if (!empty($this->defaults["module"])     &&
277 2
                !empty($this->defaults["controller"]) &&
278 2
                !empty($this->defaults["view"])
279
            ) {
280 1
                $module     = $this->defaults["module"];
281 1
                $controller = $this->defaults["controller"];
282 1
                $view       = $this->defaults["view"];
283
284 1
                $match = true;
285
            }
286
        }
287
288 6
        if (!$match) {
289 1
            throw new Exception\RouteNotFoundException("The route has not been matched");
290
        }
291
292 5
        $fqn_controller = call_user_func($this->classNameBuilder, $module, $controller);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $controller does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $module does not seem to be defined for all execution paths leading up to this point.
Loading history...
293
294 5
        if (class_exists($fqn_controller)) {
295
            try {
296 5
                $this->controller = new $fqn_controller;
297
            } catch (Exception\MethodNotFoundException $e) {
298
                # change context, in terms of Router MethodNotFoundException or
299
                # PrivateMethodExecutionException is a PageNotfoundException
300
                throw new Exception\PageNotFoundException($e->getMessage(), $e->getCode(), $e);
301
            } catch (Exception\PrivateMethodExecutionException $e) {
302
                throw new Exception\PageNotFoundException($e->getMessage(), $e->getCode(), $e);
303
            }
304
305
            # in controller terms, a view is a method
306 5
            $this->controller->setMethod($view);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $view does not seem to be defined for all execution paths leading up to this point.
Loading history...
307
        } else {
308
            throw new Exception\ControllerNotFoundException("The control class '$fqn_controller' does not exists!");
309
        }
310 5
    }
311
312
    /**
313
     * Execute the method matched in the controller
314
     *
315
     * @return mixed
316
     */
317 2
    public function run()
318
    {
319 2
        return $this->controller->execute();
320
    }
321
322
    /**
323
     * Adds a new route to router
324
     *
325
     * @param Array $route
326
     *
327
     * @throws LogicException
328
     *
329
     * @return null
330
     */
331 6
    public function addRoute(array $route)
332
    {
333 6
        $key = array_keys($route);
334
335 6
        if (count($key) > 1) {
336
            throw new \InvalidArgumentException("So many keys in a simple route");
337
        }
338
339 6
        $key = array_shift($key);
340
341 6
        $identifiers = ["module", "controller", "view"];
342
343 6
        foreach ($identifiers as $identifier) {
344 6
            if (!array_key_exists($identifier, $route[$key])) {
345
                throw new \InvalidArgumentException("The identifier '$identifier' does not exists in the route");
346
            }
347
348 6
            if (!is_string($route[$key][$identifier])) {
349 6
                throw new \InvalidArgumentException("Invalid type given for '$identifier'. String expected.");
350
            }
351
        }
352
353 6
        if (array_key_exists($key, $this->routes)) {
354
            throw new \LogicException("The key '$key' was already defined as a route");
355
        }
356
357 6
        $this->routes = array_merge($this->routes, $route);
358 6
    }
359
360
    /**
361
     * Adds a new route to router
362
     *
363
     * @param string $name
364
     * @param Zend\Router\Http\RouteInterface $route
0 ignored issues
show
Bug introduced by
The type Drone\Mvc\Zend\Router\Http\RouteInterface was not found. Did you mean Zend\Router\Http\RouteInterface? If so, make sure to prefix the type with \.
Loading history...
365
     *
366
     * @throws LogicException
367
     *
368
     * @return null
369
     */
370
    public function addZendRoute($name, \Zend\Router\Http\RouteInterface $route)
371
    {
372
        $this->zendRouter->addRoute($name, $route);
373
    }
374
375
    /**
376
     * Parse key value pairs from a string
377
     *
378
     * Searches for the pattern /var1/value1/var2/value2 and converts it to
379
     *
380
     * var1 => value1
381
     * var2 => value2
382
     *
383
     * @param string $unparsed
384
     *
385
     * @return array
386
     */
387
    private function parseKeyValuePairsFrom($unparsed)
0 ignored issues
show
Unused Code introduced by
The method parseKeyValuePairsFrom() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
388
    {
389
        $params = explode("/", $unparsed);
390
391
        $vars = $values = [];
392
393
        $i = 1;
394
        foreach ($params as $item) {
395
            if ($i % 2 != 0) {
396
                $vars[] = $item;
397
            } else {
398
                $values[] = $item;
399
            }
400
            $i++;
401
        }
402
403
        $vars_count = count($vars);
404
405
        $result = [];
406
407
        for ($i = 0; $i < $vars_count; $i++) {
408
            if (array_key_exists($i, $values)) {
409
                $result[$vars[$i]] = $values[$i];
410
            } else {
411
                $result[$vars[$i]] = '';
412
            }
413
        }
414
415
        return $result;
416
    }
417
}
418