1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Codeburner Framework. |
5
|
|
|
* |
6
|
|
|
* @author Alex Rohleder <[email protected]> |
7
|
|
|
* @copyright 2016 Alex Rohleder |
8
|
|
|
* @license http://opensource.org/licenses/MIT |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Codeburner\Router; |
12
|
|
|
|
13
|
|
|
use Codeburner\Router\Exceptions\BadRouteException; |
14
|
|
|
use Codeburner\Router\Strategies\MatcherAwareInterface; |
15
|
|
|
use Codeburner\Router\Strategies\StrategyInterface; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Route representation, a route must be able to chang and execute itself. |
19
|
|
|
* |
20
|
|
|
* @author Alex Rohleder <[email protected]> |
21
|
|
|
*/ |
22
|
|
|
|
23
|
|
|
class Route |
24
|
|
|
{ |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var Collector |
28
|
|
|
*/ |
29
|
|
|
|
30
|
|
|
protected $collector; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var string |
34
|
|
|
*/ |
35
|
|
|
|
36
|
|
|
protected $method; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var string |
40
|
|
|
*/ |
41
|
|
|
|
42
|
|
|
protected $pattern; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @var callable |
46
|
|
|
*/ |
47
|
|
|
|
48
|
|
|
protected $action; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @var string |
52
|
|
|
*/ |
53
|
|
|
|
54
|
|
|
protected $namespace = ""; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var string[] |
58
|
|
|
*/ |
59
|
|
|
|
60
|
|
|
protected $params = []; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Defaults are parameters set by the user, and don't |
64
|
|
|
* appear on the pattern. |
65
|
|
|
* |
66
|
|
|
* @var array |
67
|
|
|
*/ |
68
|
|
|
|
69
|
|
|
protected $defaults = []; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Metadata can be set to be used on filters, dispatch strategies |
73
|
|
|
* or anywhere the route object is used. |
74
|
|
|
* |
75
|
|
|
* @var array |
76
|
|
|
*/ |
77
|
|
|
|
78
|
|
|
protected $metadata = []; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* @var string|StrategyInterface |
82
|
|
|
*/ |
83
|
|
|
|
84
|
|
|
protected $strategy; |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Blocked routes are dynamic routes selected to pass by the matcher. |
88
|
|
|
* |
89
|
|
|
* @var boolean |
90
|
|
|
*/ |
91
|
|
|
|
92
|
|
|
protected $blocked = false; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* The matcher that dispatched this route. |
96
|
|
|
* |
97
|
|
|
* @var Matcher $matcher |
98
|
|
|
*/ |
99
|
|
|
|
100
|
|
|
protected $matcher; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* The function used to create controllers from name. |
104
|
|
|
* |
105
|
|
|
* @var callable |
106
|
|
|
*/ |
107
|
|
|
|
108
|
|
|
protected $controllerCreationFunction; |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @param Collector $collector |
112
|
|
|
* @param string $method |
113
|
|
|
* @param string $pattern |
114
|
|
|
* @param callable $action |
115
|
|
|
*/ |
116
|
|
|
|
117
|
50 |
|
public function __construct(Collector $collector, $method, $pattern, $action) |
118
|
|
|
{ |
119
|
50 |
|
$this->collector = $collector; |
120
|
50 |
|
$this->method = $method; |
121
|
50 |
|
$this->pattern = $pattern; |
122
|
50 |
|
$this->action = $action; |
123
|
50 |
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Clone this route and set it into the collector. |
127
|
|
|
* |
128
|
|
|
* @return Route |
129
|
|
|
*/ |
130
|
|
|
|
131
|
4 |
|
public function reset() |
132
|
|
|
{ |
133
|
4 |
|
return $this->collector->set($this->method, $this->pattern, $this->action)->nth(0) |
134
|
4 |
|
->setStrategy($this->strategy)->setParams($this->params) |
135
|
4 |
|
->setDefaults($this->defaults)->setMetadataArray($this->metadata); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Remove this route from the collector. |
140
|
|
|
* |
141
|
|
|
* @return self |
142
|
|
|
*/ |
143
|
|
|
|
144
|
6 |
|
public function forget() |
145
|
|
|
{ |
146
|
6 |
|
$this->collector->forget($this->method, $this->pattern); |
147
|
6 |
|
return $this; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Execute the route action, if no strategy was provided the action |
152
|
|
|
* will be executed by the call_user_func PHP function. |
153
|
|
|
* |
154
|
|
|
* @param callable $container |
155
|
|
|
* @throws BadRouteException |
156
|
|
|
* @return mixed |
157
|
|
|
*/ |
158
|
|
|
|
159
|
16 |
|
public function call(callable $container = null) |
160
|
|
|
{ |
161
|
16 |
|
$this->action = $this->buildCallable($this->action, $container); |
|
|
|
|
162
|
|
|
|
163
|
16 |
|
if ($this->strategy === null) { |
164
|
10 |
|
return call_user_func_array($this->action, array_merge($this->defaults, $this->params)); |
165
|
|
|
} |
166
|
|
|
|
167
|
6 |
|
if (!is_object($this->strategy)) { |
168
|
2 |
|
if ($container === null) { |
169
|
2 |
|
$this->strategy = new $this->strategy; |
170
|
2 |
|
} else $this->strategy = $container($this->strategy); |
171
|
2 |
|
} |
172
|
|
|
|
173
|
6 |
|
return $this->callWithStrategy(); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Seek for dynamic content in one callable. This allow to use parameters defined on pattern on callable |
178
|
|
|
* definition, eg. "get" "/{resource:string+}/{slug:slug+}" "{resource}::find". |
179
|
|
|
* |
180
|
|
|
* This will snakecase the resource parameter and deal with as a controller, then call the find method. |
181
|
|
|
* A request for "/articles/my-first-article" will execute find method of Articles controller with only |
182
|
|
|
* "my-first-article" as parameter. |
183
|
|
|
* |
184
|
|
|
* @param callable $callable |
185
|
|
|
* @param callable $container |
186
|
|
|
* |
187
|
|
|
* @return callable |
188
|
|
|
*/ |
189
|
|
|
|
190
|
16 |
|
private function buildCallable($callable, $container) |
191
|
|
|
{ |
192
|
16 |
|
if (is_string($callable) && strpos($callable, "::")) { |
193
|
10 |
|
$callable = explode("::", $callable); |
194
|
10 |
|
} |
195
|
|
|
|
196
|
16 |
|
if (is_array($callable)) { |
197
|
10 |
|
if (is_string($callable[0])) { |
198
|
10 |
|
$callable[0] = $this->parseCallableController($callable[0], $container); |
199
|
10 |
|
} |
200
|
|
|
|
201
|
10 |
|
$callable[1] = $this->parseCallablePlaceholders($callable[1]); |
202
|
10 |
|
} |
203
|
|
|
|
204
|
16 |
|
return $callable; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* Get the controller object. |
209
|
|
|
* |
210
|
|
|
* @param string $controller |
211
|
|
|
* @param callable $container |
212
|
|
|
* |
213
|
|
|
* @return Object |
214
|
|
|
*/ |
215
|
|
|
|
216
|
10 |
|
private function parseCallableController($controller, $container) |
217
|
|
|
{ |
218
|
10 |
|
$controller = rtrim($this->namespace, "\\") . "\\" . $this->parseCallablePlaceholders($controller); |
219
|
|
|
|
220
|
10 |
|
if ($container === null) { |
221
|
9 |
|
return new $controller; |
222
|
1 |
|
} else return $container($controller); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Parse and replace dynamic content on route action. |
227
|
|
|
* |
228
|
|
|
* @param string $fragment Part of callable |
229
|
|
|
* @return string |
230
|
|
|
*/ |
231
|
|
|
|
232
|
10 |
|
private function parseCallablePlaceholders($fragment) |
233
|
|
|
{ |
234
|
10 |
|
if (strpos($fragment, "{") !== false) { |
235
|
1 |
|
foreach ($this->params as $placeholder => $value) { |
236
|
1 |
|
if (strpos($fragment, "{" . $placeholder . "}") !== false) { |
237
|
1 |
|
$fragment = str_replace("{" . $placeholder . "}", ucwords(str_replace("-", " ", $value)), $fragment); |
238
|
1 |
|
} |
239
|
1 |
|
} |
240
|
1 |
|
} |
241
|
|
|
|
242
|
10 |
|
return $fragment; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Execute the route action with the given strategy. |
247
|
|
|
* |
248
|
|
|
* @throws BadRouteException |
249
|
|
|
* @return mixed |
250
|
|
|
*/ |
251
|
|
|
|
252
|
6 |
|
private function callWithStrategy() |
253
|
|
|
{ |
254
|
6 |
|
if ($this->strategy instanceof StrategyInterface) { |
255
|
5 |
|
if ($this->strategy instanceof MatcherAwareInterface) { |
256
|
2 |
|
$this->strategy->setMatcher($this->matcher); |
257
|
2 |
|
} |
258
|
|
|
|
259
|
5 |
|
return $this->strategy->call($this); |
260
|
|
|
} |
261
|
|
|
|
262
|
1 |
|
throw new BadRouteException(str_replace("%s", get_class($this->strategy), BadRouteException::BAD_STRATEGY)); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* @return Collector |
267
|
|
|
*/ |
268
|
|
|
|
269
|
1 |
|
public function getCollector() |
270
|
|
|
{ |
271
|
1 |
|
return $this->collector; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* @return string |
276
|
|
|
*/ |
277
|
|
|
|
278
|
|
|
public function getMethod() |
279
|
|
|
{ |
280
|
|
|
return $this->method; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* @return string |
285
|
|
|
*/ |
286
|
|
|
|
287
|
29 |
|
public function getPattern() |
288
|
|
|
{ |
289
|
29 |
|
return $this->pattern; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* @return string[] |
294
|
|
|
*/ |
295
|
|
|
|
296
|
|
|
public function getSegments() |
297
|
|
|
{ |
298
|
|
|
return explode("/", $this->pattern); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* @return callable |
303
|
|
|
*/ |
304
|
|
|
|
305
|
10 |
|
public function getAction() |
306
|
|
|
{ |
307
|
10 |
|
return $this->action; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* @return string |
312
|
|
|
*/ |
313
|
|
|
|
314
|
|
|
public function getNamespace() |
315
|
|
|
{ |
316
|
|
|
return $this->namespace; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* @return string[] |
321
|
|
|
*/ |
322
|
|
|
|
323
|
29 |
|
public function getParams() |
324
|
|
|
{ |
325
|
29 |
|
return $this->params; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* @param string $key |
330
|
|
|
* @return string |
331
|
|
|
*/ |
332
|
|
|
|
333
|
|
|
public function getParam($key) |
334
|
|
|
{ |
335
|
|
|
return $this->params[$key]; |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* Return defaults and params merged in one array. |
340
|
|
|
* |
341
|
|
|
* @return array |
342
|
|
|
*/ |
343
|
|
|
|
344
|
3 |
|
public function getMergedParams() |
345
|
|
|
{ |
346
|
3 |
|
return array_merge($this->defaults, $this->params); |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* @return array |
351
|
|
|
*/ |
352
|
|
|
|
353
|
1 |
|
public function getDefaults() |
354
|
|
|
{ |
355
|
1 |
|
return $this->defaults; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* @param string $key |
360
|
|
|
* @return mixed |
361
|
|
|
*/ |
362
|
|
|
|
363
|
1 |
|
public function getDefault($key) |
364
|
|
|
{ |
365
|
1 |
|
return $this->defaults[$key]; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* @return array |
370
|
|
|
*/ |
371
|
|
|
|
372
|
1 |
|
public function getMetadataArray() |
373
|
|
|
{ |
374
|
1 |
|
return $this->metadata; |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* @param string $key |
379
|
|
|
* @return mixed |
380
|
|
|
*/ |
381
|
|
|
|
382
|
1 |
|
public function getMetadata($key) |
383
|
|
|
{ |
384
|
1 |
|
return $this->metadata[$key]; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* @return string|null |
389
|
|
|
*/ |
390
|
|
|
|
391
|
2 |
|
public function getStrategy() |
392
|
|
|
{ |
393
|
2 |
|
if ($this->strategy instanceof StrategyInterface) { |
394
|
|
|
return get_class($this->strategy); |
395
|
|
|
} |
396
|
|
|
|
397
|
2 |
|
return $this->strategy; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
/** |
401
|
|
|
* @return StrategyInterface|string |
402
|
|
|
*/ |
403
|
|
|
|
404
|
1 |
|
public function getRawStrategy() |
405
|
|
|
{ |
406
|
1 |
|
return $this->strategy; |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* @return Matcher |
411
|
|
|
*/ |
412
|
|
|
|
413
|
1 |
|
public function getMatcher() |
414
|
|
|
{ |
415
|
1 |
|
return $this->matcher; |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
/** |
419
|
|
|
* Verify if a Route have already been blocked. |
420
|
|
|
* |
421
|
|
|
* @return boolean |
422
|
|
|
*/ |
423
|
|
|
|
424
|
29 |
|
public function getBlock() |
425
|
|
|
{ |
426
|
29 |
|
return $this->blocked; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
/** |
430
|
|
|
* Blocking a route indicate that that route have been selected and |
431
|
|
|
* parsed, now it will be given to the matcher. |
432
|
|
|
* |
433
|
|
|
* @param bool $blocked |
434
|
|
|
* @return self |
435
|
|
|
*/ |
436
|
|
|
|
437
|
29 |
|
public function setBlock($blocked) |
438
|
|
|
{ |
439
|
29 |
|
$this->blocked = $blocked; |
440
|
29 |
|
return $this; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
/** |
444
|
|
|
* @param string $method |
445
|
|
|
* @return Route |
446
|
|
|
*/ |
447
|
|
|
|
448
|
1 |
|
public function setMethod($method) |
449
|
|
|
{ |
450
|
1 |
|
$this->forget(); |
451
|
1 |
|
$this->method = $method; |
452
|
1 |
|
return $this->reset(); |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
/** |
456
|
|
|
* @param string $pattern |
457
|
|
|
* @return Route |
458
|
|
|
*/ |
459
|
|
|
|
460
|
3 |
|
public function setPattern($pattern) |
461
|
|
|
{ |
462
|
3 |
|
$this->forget(); |
463
|
3 |
|
$this->pattern = $pattern; |
464
|
3 |
|
return $this->reset(); |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
/** |
468
|
|
|
* @param string $pattern |
469
|
|
|
* @return self |
470
|
|
|
*/ |
471
|
|
|
|
472
|
29 |
|
public function setPatternWithoutReset($pattern) |
473
|
|
|
{ |
474
|
29 |
|
$this->pattern = $pattern; |
475
|
29 |
|
return $this; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* @param string $action |
480
|
|
|
* @return self |
481
|
|
|
*/ |
482
|
|
|
|
483
|
3 |
|
public function setAction($action) |
484
|
|
|
{ |
485
|
3 |
|
$this->action = $action; |
486
|
3 |
|
return $this; |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
/** |
490
|
|
|
* @param string $namespace |
491
|
|
|
* @return self |
492
|
|
|
*/ |
493
|
|
|
|
494
|
1 |
|
public function setNamespace($namespace) |
495
|
|
|
{ |
496
|
1 |
|
$this->namespace = $namespace; |
497
|
1 |
|
return $this; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
/** |
501
|
|
|
* @param string[] $params |
502
|
|
|
* @return self |
503
|
|
|
*/ |
504
|
|
|
|
505
|
30 |
|
public function setParams(array $params) |
506
|
|
|
{ |
507
|
30 |
|
$this->params = $params; |
508
|
30 |
|
return $this; |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* @param string $key |
513
|
|
|
* @param string $value |
514
|
|
|
* |
515
|
|
|
* @return self |
516
|
|
|
*/ |
517
|
|
|
|
518
|
|
|
public function setParam($key, $value) |
519
|
|
|
{ |
520
|
|
|
$this->params[$key] = $value; |
521
|
|
|
return $this; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* @param mixed[] $defaults |
526
|
|
|
* @return self |
527
|
|
|
*/ |
528
|
|
|
|
529
|
5 |
|
public function setDefaults(array $defaults) |
530
|
|
|
{ |
531
|
5 |
|
$this->defaults = $defaults; |
532
|
5 |
|
return $this; |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
/** |
536
|
|
|
* @param string $key |
537
|
|
|
* @param mixed $value |
538
|
|
|
* |
539
|
|
|
* @return self |
540
|
|
|
*/ |
541
|
|
|
|
542
|
1 |
|
public function setDefault($key, $value) |
543
|
|
|
{ |
544
|
1 |
|
$this->defaults[$key] = $value; |
545
|
1 |
|
return $this; |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
/** |
549
|
|
|
* @param mixed[] $metadata |
550
|
|
|
* @return self |
551
|
|
|
*/ |
552
|
|
|
|
553
|
5 |
|
public function setMetadataArray(array $metadata) |
554
|
|
|
{ |
555
|
5 |
|
$this->metadata = $metadata; |
556
|
5 |
|
return $this; |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
/** |
560
|
|
|
* @param string $key |
561
|
|
|
* @param mixed $value |
562
|
|
|
* |
563
|
|
|
* @return $this |
564
|
|
|
*/ |
565
|
|
|
|
566
|
1 |
|
public function setMetadata($key, $value) |
567
|
|
|
{ |
568
|
1 |
|
$this->metadata[$key] = $value; |
569
|
1 |
|
return $this; |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
/** |
573
|
|
|
* @param null|string|StrategyInterface $strategy |
574
|
|
|
* @return self |
575
|
|
|
*/ |
576
|
|
|
|
577
|
17 |
|
public function setStrategy($strategy) |
578
|
|
|
{ |
579
|
17 |
|
$this->strategy = $strategy; |
580
|
17 |
|
return $this; |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
/** |
584
|
|
|
* @param Matcher $matcher |
585
|
|
|
* @return self |
586
|
|
|
*/ |
587
|
|
|
|
588
|
42 |
|
public function setMatcher(Matcher $matcher) |
589
|
|
|
{ |
590
|
42 |
|
$this->matcher = $matcher; |
591
|
42 |
|
return $this; |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
/** |
595
|
|
|
* Set a constraint to a token in the route pattern. |
596
|
|
|
* |
597
|
|
|
* @param string $token |
598
|
|
|
* @param string $regex |
599
|
|
|
* |
600
|
|
|
* @return self |
601
|
|
|
*/ |
602
|
|
|
|
603
|
1 |
|
public function setConstraint($token, $regex) |
604
|
|
|
{ |
605
|
1 |
|
$initPos = strpos($this->pattern, "{" . $token); |
606
|
|
|
|
607
|
1 |
|
if ($initPos !== false) { |
608
|
1 |
|
$endPos = strpos($this->pattern, "}", $initPos); |
609
|
1 |
|
$newPattern = substr_replace($this->pattern, "{" . "$token:$regex" . "}", $initPos, $endPos - $initPos + 1); |
610
|
1 |
|
$wildcards = $this->collector->getParser()->getWildcardTokens(); |
611
|
1 |
|
$newPattern = str_replace(array_keys($wildcards), $wildcards, $newPattern); |
612
|
1 |
|
$this->setPatternWithoutReset($newPattern); |
613
|
1 |
|
} |
614
|
|
|
|
615
|
1 |
|
return $this; |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
/** |
619
|
|
|
* @param string $key |
620
|
|
|
* @return bool |
621
|
|
|
*/ |
622
|
|
|
|
623
|
|
|
public function hasParam($key) |
624
|
|
|
{ |
625
|
|
|
return isset($this->params[$key]); |
626
|
|
|
} |
627
|
|
|
|
628
|
|
|
/** |
629
|
|
|
* @param string $key |
630
|
|
|
* @return bool |
631
|
|
|
*/ |
632
|
|
|
|
633
|
|
|
public function hasDefault($key) |
634
|
|
|
{ |
635
|
|
|
return isset($this->defaults[$key]); |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
/** |
639
|
|
|
* @param string $key |
640
|
|
|
* @return bool |
641
|
|
|
*/ |
642
|
|
|
|
643
|
|
|
public function hasMetadata($key) |
644
|
|
|
{ |
645
|
|
|
return isset($this->metadata[$key]); |
646
|
|
|
} |
647
|
|
|
|
648
|
|
|
} |
649
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.