1
|
|
|
<?php namespace Anomaly\Streams\Platform\Support; |
2
|
|
|
|
3
|
|
|
use ArrayAccess; |
4
|
|
|
use Illuminate\Contracts\Container\Container; |
5
|
|
|
|
6
|
|
|
/** |
7
|
|
|
* Class Evaluator |
8
|
|
|
* |
9
|
|
|
* @link http://anomaly.is/streams-platform |
10
|
|
|
* @author AnomalyLabs, Inc. <[email protected]> |
11
|
|
|
* @author Ryan Thompson <[email protected]> |
12
|
|
|
* @package Anomaly\Streams\Platform\Support |
13
|
|
|
*/ |
14
|
|
|
class Evaluator |
15
|
|
|
{ |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* The IoC container. |
19
|
|
|
* |
20
|
|
|
* @var \Illuminate\Contracts\Container\Container |
21
|
|
|
*/ |
22
|
|
|
protected $container; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Create a new Evaluator instance. |
26
|
|
|
* |
27
|
|
|
* @param Container $container |
28
|
|
|
*/ |
29
|
|
|
public function __construct(Container $container) |
30
|
|
|
{ |
31
|
|
|
$this->container = $container; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Evaluate a target entity with arguments. |
36
|
|
|
* |
37
|
|
|
* @param $target |
38
|
|
|
* @param array $arguments |
39
|
|
|
* @return mixed |
40
|
|
|
*/ |
41
|
|
|
public function evaluate($target, array $arguments = []) |
42
|
|
|
{ |
43
|
|
|
/** |
44
|
|
|
* If the target is an instance of Closure then |
45
|
|
|
* call through the IoC it with the arguments. |
46
|
|
|
*/ |
47
|
|
|
if ($target instanceof \Closure) { |
48
|
|
|
return $this->container->call($target, $arguments); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* If the target is an array then evaluate |
53
|
|
|
* each of it's values. |
54
|
|
|
*/ |
55
|
|
|
if (is_array($target)) { |
56
|
|
|
foreach ($target as &$value) { |
57
|
|
|
$value = $this->evaluate($value, $arguments); |
58
|
|
|
} |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* if the target is a string and is in a traversable |
63
|
|
|
* format then traverse the target using the arguments. |
64
|
|
|
*/ |
65
|
|
|
if (is_string($target) && !isset($arguments[$target]) && $this->isTraversable($target)) { |
66
|
|
|
$target = $this->data($arguments, $target, $target); |
|
|
|
|
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
return $target; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Check if a string is in a traversable format. |
74
|
|
|
* |
75
|
|
|
* @param $target |
76
|
|
|
* @return bool |
77
|
|
|
*/ |
78
|
|
|
protected function isTraversable($target) |
79
|
|
|
{ |
80
|
|
|
return (!preg_match('/[^a-z._]/', $target)); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Return the data from the key. |
85
|
|
|
* |
86
|
|
|
* @param $arguments |
87
|
|
|
* @param $target |
88
|
|
|
* @return mixed |
89
|
|
|
*/ |
90
|
|
|
protected function data($arguments, $target) |
91
|
|
|
{ |
92
|
|
|
if (is_null($arguments)) { |
93
|
|
|
return $target; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$arguments = is_array($arguments) ? $arguments : explode('.', $arguments); |
97
|
|
|
|
98
|
|
|
foreach ($arguments as $segment) { |
99
|
|
|
if (is_array($target)) { |
100
|
|
|
if (!array_key_exists($segment, $target)) { |
101
|
|
|
return value($target); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
$target = $target[$segment]; |
105
|
|
|
} elseif ($target instanceof ArrayAccess) { |
106
|
|
|
if (!isset($target[$segment])) { |
107
|
|
|
return value($target); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
$target = $target[$segment]; |
111
|
|
|
} elseif (is_object($target)) { |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Check if the method exists first |
115
|
|
|
* otherwise we might end up trying to |
116
|
|
|
* access relational methods. |
117
|
|
|
* |
118
|
|
|
* Otherwise this is identical from |
119
|
|
|
* data_get helper. |
120
|
|
|
*/ |
121
|
|
|
if (method_exists($target, $segment) || !isset($target->{$segment})) { |
122
|
|
|
return value($target); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
$target = $target->{$segment}; |
126
|
|
|
} else { |
127
|
|
|
return value($target); |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
return $target; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.