1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | /** |
||||
6 | * Micro |
||||
7 | * |
||||
8 | * @author Raffael Sahli <[email protected]> |
||||
9 | * @copyright Copryright (c) 2015-2018 gyselroth GmbH (https://gyselroth.com) |
||||
10 | * @license MIT https://opensource.org/licenses/MIT |
||||
11 | */ |
||||
12 | |||||
13 | namespace Micro\Http\Router; |
||||
14 | |||||
15 | use Micro\Http\Router; |
||||
16 | use Psr\Container\ContainerInterface; |
||||
17 | |||||
18 | class Route |
||||
19 | { |
||||
20 | /** |
||||
21 | * Router. |
||||
22 | * |
||||
23 | * @var Router |
||||
24 | */ |
||||
25 | protected $router; |
||||
26 | |||||
27 | /** |
||||
28 | * Continue propagation if match. |
||||
29 | * |
||||
30 | * @var bool |
||||
31 | */ |
||||
32 | protected $continue_propagation = false; |
||||
33 | |||||
34 | /** |
||||
35 | * Route path. |
||||
36 | * |
||||
37 | * @var string |
||||
38 | */ |
||||
39 | protected $path; |
||||
40 | |||||
41 | /** |
||||
42 | * Class to be instanced if route match was found. |
||||
43 | * |
||||
44 | * @var object|string |
||||
45 | */ |
||||
46 | protected $class; |
||||
47 | |||||
48 | /** |
||||
49 | * Method to be executed if route match was found. |
||||
50 | * |
||||
51 | * @var string |
||||
52 | */ |
||||
53 | protected $method; |
||||
54 | |||||
55 | /** |
||||
56 | * Found request parameters. |
||||
57 | * |
||||
58 | * @var array |
||||
59 | */ |
||||
60 | protected $params = []; |
||||
61 | |||||
62 | /** |
||||
63 | * Instance route. |
||||
64 | * |
||||
65 | * @param string $path |
||||
66 | * @param string $class |
||||
67 | * @param string $method |
||||
68 | * @param array $params |
||||
69 | */ |
||||
70 | public function __construct(string $path, $class, ? string $method = null, array $params = []) |
||||
71 | { |
||||
72 | $this->setPath($path); |
||||
73 | $this->setClass($class); |
||||
74 | $this->setMethod($method); |
||||
75 | $this->setParams($params); |
||||
76 | } |
||||
77 | |||||
78 | /** |
||||
79 | * Check if route does match the current http request. |
||||
80 | * |
||||
81 | * @return bool |
||||
82 | */ |
||||
83 | public function match(): bool |
||||
84 | { |
||||
85 | $regex = preg_replace_callback('#({([A-Z0-9a-z]+)\:\#(.+?)\#})|\{(.+?)\}#', function ($match) { |
||||
86 | if (4 === count($match)) { |
||||
87 | return '(?<'.$match[2].'>'.$match[3].'+)'; |
||||
88 | } |
||||
89 | |||||
90 | return '(?<'.end($match).'>\w+)'; |
||||
91 | }, $this->path); |
||||
92 | |||||
93 | if (preg_match('#^'.$regex.'#', $this->router->getPath(), $matches)) { |
||||
94 | foreach ($matches as $key => $value) { |
||||
95 | if (!is_int($key)) { |
||||
96 | $this->params[$key] = $value; |
||||
97 | } |
||||
98 | } |
||||
99 | |||||
100 | return true; |
||||
101 | } |
||||
102 | |||||
103 | return false; |
||||
104 | } |
||||
105 | |||||
106 | /** |
||||
107 | * Get callable. |
||||
108 | * |
||||
109 | * @param ContainerInterface $container |
||||
110 | * |
||||
111 | * @return array |
||||
112 | */ |
||||
113 | public function getCallable(?ContainerInterface $container = null): array |
||||
114 | { |
||||
115 | if (is_object($this->class)) { |
||||
116 | $instance = $this->class; |
||||
117 | } else { |
||||
118 | if (null === $container) { |
||||
119 | $instance = new $this->class(); |
||||
120 | } else { |
||||
121 | $instance = $container->get($this->class); |
||||
122 | } |
||||
123 | } |
||||
124 | |||||
125 | if (null !== $this->method) { |
||||
126 | return [&$instance, $this->method]; |
||||
127 | } |
||||
128 | |||||
129 | return [&$instance, $this->getMethod()]; |
||||
130 | } |
||||
131 | |||||
132 | /** |
||||
133 | * Get http router. |
||||
134 | * |
||||
135 | * @return Router |
||||
136 | */ |
||||
137 | public function getRouter(): Router |
||||
138 | { |
||||
139 | return $this->router; |
||||
140 | } |
||||
141 | |||||
142 | /** |
||||
143 | * Set http router. |
||||
144 | * |
||||
145 | * @param Router $router |
||||
146 | * |
||||
147 | * @return Route |
||||
148 | */ |
||||
149 | public function setRouter(Router $router): self |
||||
150 | { |
||||
151 | $this->router = $router; |
||||
152 | |||||
153 | return $this; |
||||
154 | } |
||||
155 | |||||
156 | /** |
||||
157 | * Get path. |
||||
158 | * |
||||
159 | * @return string |
||||
160 | */ |
||||
161 | public function getPath(): string |
||||
162 | { |
||||
163 | return $this->path; |
||||
164 | } |
||||
165 | |||||
166 | /** |
||||
167 | * Set path. |
||||
168 | * |
||||
169 | * @param string $path |
||||
170 | * |
||||
171 | * @return Route |
||||
172 | */ |
||||
173 | public function setPath(string $path): self |
||||
174 | { |
||||
175 | $this->path = (string) $path; |
||||
176 | |||||
177 | return $this; |
||||
178 | } |
||||
179 | |||||
180 | /** |
||||
181 | * Get class. |
||||
182 | * |
||||
183 | * @return string |
||||
184 | */ |
||||
185 | public function getClass(): string |
||||
186 | { |
||||
187 | if (is_object($this->class)) { |
||||
188 | return get_class($this->class); |
||||
189 | } |
||||
190 | |||||
191 | return $this->class; |
||||
192 | } |
||||
193 | |||||
194 | /** |
||||
195 | * Set clas. |
||||
196 | * |
||||
197 | * @param object|string $class |
||||
198 | * |
||||
199 | * @return Route |
||||
200 | */ |
||||
201 | public function setClass($class): self |
||||
202 | { |
||||
203 | $this->class = $class; |
||||
204 | |||||
205 | return $this; |
||||
206 | } |
||||
207 | |||||
208 | /** |
||||
209 | * Get method. |
||||
210 | * |
||||
211 | * @return string |
||||
212 | */ |
||||
213 | public function getMethod(): string |
||||
214 | { |
||||
215 | if ('$' === substr($this->path, -1)) { |
||||
216 | return $this->router->getVerb(); |
||||
217 | } |
||||
218 | |||||
219 | $path = $this->router->getPath(); |
||||
220 | $action = substr($path, strrpos($path, '/') + 1); |
||||
221 | |||||
222 | if (!in_array($action, $this->params, true)) { |
||||
223 | $this->method = $action; |
||||
224 | } |
||||
225 | |||||
226 | $short = $this->camelCase2Dashes(substr($this->class, strrpos($this->class, '\\') + 1)); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() It seems like
$this->class can also be of type object ; however, parameter $string of substr() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
227 | |||||
228 | if ($this->camelCase2Dashes($short) === $action) { |
||||
229 | $this->method = ''; |
||||
230 | } |
||||
231 | |||||
232 | return $this->buildMethodName($this->method); |
||||
233 | } |
||||
234 | |||||
235 | /** |
||||
236 | * Set method. |
||||
237 | * |
||||
238 | * @param string $method |
||||
239 | * |
||||
240 | * @return Route |
||||
241 | */ |
||||
242 | public function setMethod(? string $method): self |
||||
243 | { |
||||
244 | $this->method = $method; |
||||
245 | |||||
246 | return $this; |
||||
247 | } |
||||
248 | |||||
249 | /** |
||||
250 | * Set params. |
||||
251 | * |
||||
252 | * @param array $params |
||||
253 | * |
||||
254 | * @return Route |
||||
255 | */ |
||||
256 | public function setParams(array $params): self |
||||
257 | { |
||||
258 | $this->params = $params; |
||||
259 | |||||
260 | return $this; |
||||
261 | } |
||||
262 | |||||
263 | /** |
||||
264 | * Get translated regex variable values. |
||||
265 | * |
||||
266 | * @return array |
||||
267 | */ |
||||
268 | public function getParams(): array |
||||
269 | { |
||||
270 | return $this->params; |
||||
271 | } |
||||
272 | |||||
273 | /** |
||||
274 | * Continue propagation after match. |
||||
275 | * |
||||
276 | * @return bool |
||||
277 | */ |
||||
278 | public function continueAfterMatch(): bool |
||||
279 | { |
||||
280 | return $this->continue_propagation; |
||||
281 | } |
||||
282 | |||||
283 | /** |
||||
284 | * Halt propagation after match. |
||||
285 | * |
||||
286 | * @param bool $next |
||||
287 | * |
||||
288 | * @return Route |
||||
289 | */ |
||||
290 | public function continuePropagation($next = true): self |
||||
291 | { |
||||
292 | $this->continue_propagation = (bool) $next; |
||||
293 | |||||
294 | return $this; |
||||
295 | } |
||||
296 | |||||
297 | /** |
||||
298 | * Build method name. |
||||
299 | * |
||||
300 | * @param string $name |
||||
301 | * |
||||
302 | * @return string |
||||
303 | */ |
||||
304 | protected function buildMethodName(? string $name): string |
||||
305 | { |
||||
306 | $result = $this->router->getVerb(); |
||||
307 | |||||
308 | if (null !== $name) { |
||||
309 | $split = explode('-', $name); |
||||
310 | foreach ($split as $part) { |
||||
311 | $result .= ucfirst($part); |
||||
312 | } |
||||
313 | } |
||||
314 | |||||
315 | return $result; |
||||
316 | } |
||||
317 | |||||
318 | /** |
||||
319 | * Convert camelCase to dashes. |
||||
320 | * |
||||
321 | * @param string $value |
||||
322 | * |
||||
323 | * @return string |
||||
324 | */ |
||||
325 | protected function camelCase2Dashes(string $value): string |
||||
326 | { |
||||
327 | return strtolower(preg_replace('/([a-z])([A-Z])/', '$1-$2', $value)); |
||||
328 | } |
||||
329 | } |
||||
330 |