Passed
Branch master (32ad63)
by Bohuslav
02:06
created

ClosureAutoBind::dispatchRoute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 10
cts 10
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 10
nc 2
nop 2
crap 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Kambo\Router\Dispatcher;
5
6
// \Spl
7
use Closure;
8
use InvalidArgumentException;
9
use ReflectionFunction;
10
11
// \Kambo\Router
12
use Kambo\Router\Dispatcher;
13
use Kambo\Router\Route\Route\Parsed;
14
15
/**
16
 * Dispatcher with closure support
17
 *
18
 * @package Kambo\Router\Dispatcher
19
 * @author  Bohuslav Simek <[email protected]>
20
 * @license MIT
21
 */
22
class ClosureAutoBind implements Dispatcher
23
{
24
    /**
25
     * Not found handler will be called if nothing has been found.
26
     *
27
     * @var \Closure
28
     */
29
    private $notFoundHandler;
30
31
    /**
32
     * Dispatch found route with given parameters
33
     *
34
     * @param \Kambo\Router\Route\Route\Parsed $route      Instance of found and parsed route.
35
     * @param array                            $parameters Additional parameters.
36
     *
37
     * @return mixed|null
38
     */
39 8
    public function dispatchRoute(Parsed $route, array $parameters = [])
40
    {
41 8
        $handler = $route->getHandler();
42 8
        if ($this->isClosure($handler)) {
43 7
            $paramMap  = $this->getFunctionArgumentsNames($handler);
44 7
            $arguments = $this->getFunctionArguments(
45 7
                $paramMap,
46 7
                $route->getParameters(),
47 7
                $route->getPlaceholders()
48
            );
49
50 7
            return call_user_func_array($handler, $arguments);
51
        }
52
53 1
        return $this->dispatchNotFound();
54
    }
55
56
    /**
57
     * Called if any of route did not match the request.
58
     * Call the defined handler or simply do nothing if the handler is not
59
     * specified.
60
     *
61
     * @return mixed|null
62
     */
63 5
    public function dispatchNotFound()
64
    {
65 5
        if (isset($this->notFoundHandler)) {
66 3
            return call_user_func($this->notFoundHandler);
67
        }
68
69 2
        return null;
70
    }
71
72
    /**
73
     * Sets not found handler
74
     *
75
     * @param Closure $handler handler that will be excuted if nothing has been
76
     *                        found
77
     *
78
     * @return self for fluent interface
79
     *
80
     * @throws InvalidArgumentException if the provided value is not closure
81
     */
82 5
    public function setNotFoundHandler($handler) : ClosureAutoBind
83
    {
84 5
        if (!$this->isClosure($handler)) {
85 1
            throw new InvalidArgumentException(
86 1
                'Handler must be closure'
87
            );
88
        }
89
90 4
        $this->notFoundHandler = $handler;
91
92 4
        return $this;
93
    }
94
95
    // ------------ PRIVATE METHODS
96
97
    /**
98
     * Check if variable is closure
99
     *
100
     * @param mixed $type variable to check
101
     *
102
     * @return boolean return true if is
103
     */
104 13
    private function isClosure($type) : bool
105
    {
106 13
        return is_object($type) && ($type instanceof Closure);
107
    }
108
109
    /**
110
     * Get arguments for closure function in proper order
111
     * from provided parameters
112
     *
113
     * @param array $paramMap   parameter map for getting proper order
114
     * @param array $matches    parameters from request
115
     * @param array $parameters expected parameters from route
116
     *
117
     * @return array Parameters in right order, if there are not any
118
     *               parametrs an empty array is returned.
119
     */
120 7
    private function getFunctionArguments(array $paramMap, array $matches, array $parameters) : array
121
    {
122 7
        $output  = [];
123 7
        $matches = array_values($matches);
124
125 7 View Code Duplication
        foreach ($parameters as $valueName) {
126 3
            foreach ($paramMap as $possition => $value) {
127 3
                if ($value == $valueName[1][0]) {
128 3
                    $output[] = $matches[$possition];
129
                }
130
            }
131
        }
132
133 7
        return $output;
134
    }
135
136
    /**
137
     * Get name of parameters for provided closure
138
     *
139
     * @param \Closure $closure
140
     *
141
     * @return array
142
     */
143 7
    private function getFunctionArgumentsNames($closure) : array
144
    {
145 7
        $result = [];
146
147 7
        $closureReflection = new ReflectionFunction($closure);
148
149 7
        foreach ($closureReflection->getParameters() as $param) {
150 3
            $result[] = $param->name;
151
        }
152
153 7
        return $result;
154
    }
155
}
156