Passed
Push — release/0.4.1 ( 076d5f )
by Mathieu
04:35
created

Route::getRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Suricate;
6
7
use ReflectionMethod;
8
use ReflectionFunction;
9
10
class Route
11
{
12
    private $name;
13
    private $method = [];
14
    private $path;
15
    private $computedPath;
16
17
    /** @var Request $request */
18
    private $request;
19
20
    private $parametersDefinitions;
21
    public $parametersValues;
22
23
    public $isMatched;
24
    public $target;
25
    public $middlewares = [];
26
27
    /**
28
     * Route constructor
29
     *
30
     * @param string        $name                  Route name
31
     * @param string|array  $method                Method accepted for route
32
     * @param string        $path                  Route path
33
     * @param Request       $request               Request
34
     * @param array|null    $routeTarget           Route target
35
     * @param array         $parametersDefinitions Parameters definition
36
     * @param mixed         $middleware            Middleware
37
     */
38
    public function __construct(
39
        $name,
40
        $method,
41
        $path,
42
        Request $request,
43
        $routeTarget,
44
        $parametersDefinitions = [],
45
        $middleware = null
46
    ) {
47
        $this->isMatched = false;
48
        $this->name = $name;
49
        $this->method = array_map('strtolower', (array) $method);
50
        $this->path = $path;
51
        $this->request = $request;
52
        $this->target = $routeTarget;
53
        $this->parametersDefinitions = $parametersDefinitions;
54
        $this->parametersValues = [];
55
        $this->middlewares = (array) $middleware;
56
57
        $this->setParameters();
58
        $this->computePath();
59
        $this->match();
60
    }
61
62
    /**
63
     * Get route name
64
     *
65
     * @return string
66
     */
67
    public function getName(): string
68
    {
69
        return $this->name;
70
    }
71
72
    /**
73
     * Get route path
74
     *
75
     * @return string
76
     */
77
    public function getPath(): string
78
    {
79
        return $this->path;
80
    }
81
82
    /**
83
     * Get route method
84
     *
85
     * @return array
86
     */
87
    public function getMethod(): array
88
    {
89
        return $this->method;
90
    }
91
92
    /**
93
     * Get parameters definition
94
     *
95
     * @return array
96
     */
97
    public function getParameters(): array
98
    {
99
        return $this->parametersDefinitions;
100
    }
101
102
    /**
103
     * Get route target
104
     *
105
     * @return array|null
106
     */
107
    public function getTarget(): ?array
108
    {
109
        return $this->target;
110
    }
111
112
    /**
113
     * Get HTTP request
114
     *
115
     * @return Request
116
     */
117
    public function getRequest(): Request
118
    {
119
        return $this->request;
120
    }
121
122
    private function match()
123
    {
124
        $requestUri = $this->request->getRequestUri();
125
        $pos = strpos($requestUri, '?');
126
        if ($pos !== false) {
127
            $requestUri = substr($requestUri, 0, $pos);
128
        }
129
130
        if (
131
            $this->method === ['any'] ||
132
            in_array(strtolower($this->request->getMethod()), $this->method)
133
        ) {
134
            // requestUri is matching pattern, set as matched route
135
            if (
136
                preg_match(
137
                    '#^' . $this->computedPath . '$#',
138
                    $requestUri,
139
                    $matching
140
                )
141
            ) {
142
                foreach (
143
                    array_keys($this->parametersDefinitions)
144
                    as $currentParameter
145
                ) {
146
                    $this->parametersValues[$currentParameter] = isset(
147
                        $matching[$currentParameter]
148
                    )
149
                        ? $matching[$currentParameter]
150
                        : null;
151
                }
152
153
                $this->isMatched = true;
154
            }
155
        }
156
    }
157
158
    public function dispatch($response, $middlewares = [])
159
    {
160
        $result = false;
161
        $callable = $this->getCallable($response);
162
        if (is_callable($callable)) {
163
            $this->middlewares = array_merge($middlewares, $this->middlewares);
164
165
            // We found a valid method for this controller
166
            // Find parameters order
167
            $methodArguments = $this->getCallableArguments();
168
169
            // Calling $controller->method with arguments in right order
170
171
            // Middleware stack processing
172
            foreach ($this->middlewares as $middleware) {
173
                if (
174
                    is_object($middleware) &&
175
                    $middleware instanceof Middleware
176
                ) {
177
                    $middleware->call($this->request, $response);
178
                } else {
179
                    with(new $middleware())->call($this->request, $response);
180
                }
181
            }
182
183
            $result = call_user_func_array($callable, $methodArguments);
184
        }
185
186
        return $result;
187
    }
188
189
    private function getCallable($response)
190
    {
191
        if (count($this->target) > 1) {
192
            $callable = [
193
                new $this->target[0]($response, $this),
194
                $this->target[1]
195
            ];
196
        } else {
197
            $callable = $this->target;
198
        }
199
200
        return $callable;
201
    }
202
203
    private function getCallableArguments()
204
    {
205
        if (count($this->target) > 1) {
206
            $reflection = new ReflectionMethod(
207
                $this->target[0],
208
                $this->target[1]
209
            );
210
        } else {
211
            $reflection = new ReflectionFunction($this->target);
212
        }
213
214
        $methodParameters = $reflection->getParameters();
215
        $methodArguments = [];
216
217
        foreach ($methodParameters as $index => $parameter) {
218
            if (isset($this->parametersValues[$parameter->name])) {
219
                $methodArguments[$index] = urldecode(
220
                    $this->parametersValues[$parameter->name]
221
                );
222
            } else {
223
                // No value matching this parameter
224
                $methodArguments[$index] = null;
225
            }
226
        }
227
228
        return $methodArguments;
229
    }
230
231
    protected function setParameters()
232
    {
233
        // Get all route parameters
234
        preg_match_all('|:([\w]+)|', $this->path, $routeParameters);
235
        $routeParametersNames = $routeParameters[1];
236
237
        foreach ($routeParametersNames as $parameter) {
238
            // Patterns parameters are not set, considering implicit declaration
239
            if (!isset($this->parametersDefinitions[$parameter])) {
240
                $this->parametersDefinitions[$parameter] = '.*';
241
            }
242
        }
243
    }
244
245
    /**
246
     * Build PCRE pattern path, according to route parameters
247
     * @return null
248
     */
249
    protected function computePath()
250
    {
251
        $this->computedPath = $this->path;
252
253
        // Assigning parameters
254
        foreach (
255
            $this->parametersDefinitions
256
            as $parameterName => $parameterDefinition
257
        ) {
258
            $this->computedPath = str_replace(
259
                ':' . $parameterName,
260
                '(?<' . $parameterName . '>' . $parameterDefinition . ')',
261
                $this->computedPath
262
            );
263
        }
264
    }
265
}
266