1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* PHP Smart Analysis project 2015-2016 |
4
|
|
|
* |
5
|
|
|
* @author Patsura Dmitry https://github.com/ovr <[email protected]> |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace PHPSA\Compiler\Expression; |
9
|
|
|
|
10
|
|
|
use Ovr\PHPReflection\Reflector; |
11
|
|
|
use PHPSA\CompiledExpression; |
12
|
|
|
use PHPSA\Context; |
13
|
|
|
use PHPSA\Compiler\Expression; |
14
|
|
|
use PHPSA\Definition\ClosureDefinition; |
15
|
|
|
|
16
|
|
|
class FunctionCall extends AbstractExpressionCompiler |
17
|
|
|
{ |
18
|
|
|
protected $name = 'PhpParser\Node\Expr\FuncCall'; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @param \PhpParser\Node\Expr\FuncCall $expr |
22
|
|
|
* @param Context $context |
23
|
|
|
* @return CompiledExpression |
24
|
|
|
*/ |
25
|
7 |
|
protected function compile($expr, Context $context) |
|
|
|
|
26
|
|
|
{ |
27
|
7 |
|
$expressionCompiler = $context->getExpressionCompiler(); |
28
|
7 |
|
$fNameExpression = $expressionCompiler->compile($expr->name); |
29
|
|
|
|
30
|
7 |
|
if ($fNameExpression->isCallable()) { |
31
|
|
|
$value = $fNameExpression->getValue(); |
32
|
|
|
if ($value && $value instanceof ClosureDefinition) { |
33
|
|
|
return $value->run($this->parseArgs($expr, clone $context), $context); |
34
|
|
|
} |
35
|
|
|
} |
36
|
|
|
|
37
|
7 |
|
if ($fNameExpression->isString() && $fNameExpression->isCorrectValue()) { |
38
|
7 |
|
$name = $fNameExpression->getValue(); |
39
|
7 |
|
} else { |
40
|
1 |
|
$context->debug( |
41
|
|
|
'Unexpected function name type ' . $fNameExpression->getTypeName(), |
42
|
|
|
$expr->name |
43
|
|
|
); |
44
|
|
|
|
45
|
|
|
return new CompiledExpression; |
46
|
|
|
} |
47
|
|
|
|
48
|
7 |
|
$compiler = $context->application->compiler; |
49
|
|
|
|
50
|
7 |
|
$exists = false; |
51
|
7 |
|
$namespace = null; |
52
|
|
|
|
53
|
7 |
|
if ($context->scope) { |
54
|
7 |
|
$namespace = $context->scope->getNamespace(); |
55
|
7 |
|
} |
56
|
|
|
|
57
|
7 |
|
if ($namespace === null) { |
58
|
|
|
$functionDefinition = $compiler->getFunction($name); |
59
|
|
|
} else { |
60
|
7 |
|
$functionDefinition = $compiler->getFunctionNS($name, $namespace); |
61
|
|
|
} |
62
|
|
|
|
63
|
7 |
|
if (!$functionDefinition) { |
64
|
7 |
|
$exists = function_exists($name); |
65
|
7 |
|
} |
66
|
|
|
|
67
|
7 |
|
if ($functionDefinition) { |
68
|
|
|
if (!$functionDefinition->isCompiled()) { |
69
|
|
|
$functionDefinition->compile(clone $context); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
$exists = true; |
73
|
|
|
} |
74
|
|
|
|
75
|
7 |
|
$arguments = $this->parseArgs($expr, clone $context); |
76
|
|
|
|
77
|
7 |
|
if (!$functionDefinition) { |
78
|
7 |
|
$reflector = new Reflector(Reflector::manuallyFactory()); |
79
|
7 |
|
$functionReflection = $reflector->getFunction($name); |
80
|
7 |
|
if ($functionReflection) { |
81
|
3 |
|
$argumentsSuccessPass = true; |
82
|
|
|
|
83
|
3 |
|
if (count($arguments) > 0) { |
84
|
3 |
|
foreach ($arguments as $key => $argument) { |
85
|
3 |
|
$parameter = $functionReflection->getParameter($key); |
86
|
3 |
|
if (!$parameter) { |
87
|
|
|
$argumentsSuccessPass = false; |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @todo Think a little bit more about it |
91
|
|
|
*/ |
92
|
|
|
continue; |
93
|
|
|
} |
94
|
|
|
|
95
|
3 |
|
switch ($parameter->getType()) { |
96
|
3 |
|
case CompiledExpression::MIXED: |
97
|
|
|
//continue |
98
|
2 |
|
break; |
99
|
2 |
|
case CompiledExpression::INTEGER: |
100
|
2 |
|
switch ($argument->getType()) { |
101
|
2 |
|
case CompiledExpression::INTEGER: |
102
|
2 |
|
break; |
103
|
|
|
default: |
104
|
|
|
$argumentsSuccessPass = false; |
105
|
|
|
break; |
106
|
2 |
|
} |
107
|
2 |
|
break; |
108
|
|
|
case CompiledExpression::DOUBLE: |
109
|
|
|
switch ($argument->getType()) { |
110
|
|
|
case CompiledExpression::DOUBLE: |
111
|
|
|
break; |
112
|
|
|
default: |
113
|
|
|
$argumentsSuccessPass = false; |
114
|
|
|
break; |
115
|
|
|
} |
116
|
|
|
break; |
117
|
|
|
case CompiledExpression::NUMBER: |
118
|
|
|
switch ($argument->getType()) { |
119
|
|
|
case CompiledExpression::INTEGER: |
120
|
|
|
case CompiledExpression::STRING: |
121
|
|
|
case CompiledExpression::NUMBER: |
122
|
|
|
break; |
123
|
|
|
default: |
124
|
|
|
$argumentsSuccessPass = false; |
125
|
|
|
break; |
126
|
|
|
} |
127
|
|
|
break; |
128
|
|
|
case CompiledExpression::RESOURCE: |
129
|
|
|
switch ($argument->getType()) { |
130
|
|
|
case CompiledExpression::RESOURCE: |
131
|
|
|
break; |
132
|
|
|
default: |
133
|
|
|
$argumentsSuccessPass = false; |
134
|
|
|
break; |
135
|
|
|
} |
136
|
|
|
break; |
137
|
|
|
case CompiledExpression::ARR: |
138
|
|
|
switch ($argument->getType()) { |
139
|
|
|
case CompiledExpression::ARR: |
140
|
|
|
break; |
141
|
|
|
default: |
142
|
|
|
$argumentsSuccessPass = false; |
143
|
|
|
break; |
144
|
|
|
} |
145
|
|
|
break; |
146
|
|
|
case CompiledExpression::STRING: |
147
|
|
|
switch ($argument->getType()) { |
148
|
|
|
case CompiledExpression::STRING: |
149
|
|
|
break; |
150
|
|
|
default: |
151
|
|
|
$argumentsSuccessPass = false; |
152
|
|
|
break; |
153
|
|
|
} |
154
|
|
|
break; |
155
|
|
|
case CompiledExpression::OBJECT: |
156
|
|
|
switch ($argument->getType()) { |
157
|
|
|
case CompiledExpression::OBJECT: |
158
|
|
|
break; |
159
|
|
|
default: |
160
|
|
|
$argumentsSuccessPass = false; |
161
|
|
|
break; |
162
|
|
|
} |
163
|
|
|
break; |
164
|
|
|
case CompiledExpression::BOOLEAN: |
165
|
|
|
switch ($argument->getType()) { |
166
|
|
|
case CompiledExpression::OBJECT: |
167
|
|
|
break; |
168
|
|
|
default: |
169
|
|
|
$argumentsSuccessPass = false; |
170
|
|
|
break; |
171
|
|
|
} |
172
|
|
|
break; |
173
|
|
|
case CompiledExpression::CALLABLE_TYPE: |
174
|
|
|
switch ($argument->getType()) { |
175
|
|
|
case CompiledExpression::CALLABLE_TYPE: |
176
|
|
|
break; |
177
|
|
|
case CompiledExpression::STRING: |
178
|
|
|
/** |
179
|
|
|
* @todo We need additional check on it |
180
|
|
|
*/ |
181
|
|
|
break; |
182
|
|
|
/** |
183
|
|
|
* array($this, 'method') |
184
|
|
|
*/ |
185
|
|
|
case CompiledExpression::ARR: |
186
|
|
|
/** |
187
|
|
|
* @todo We need additional check on it |
188
|
|
|
*/ |
189
|
|
|
break; |
190
|
|
|
default: |
191
|
|
|
$argumentsSuccessPass = false; |
192
|
|
|
break; |
193
|
|
|
} |
194
|
|
|
break; |
195
|
|
|
default: |
196
|
|
|
$argumentsSuccessPass = false; |
197
|
|
|
break; |
198
|
3 |
|
} |
199
|
3 |
|
} |
200
|
3 |
|
} |
201
|
|
|
|
202
|
3 |
|
if (count($arguments) < $functionReflection->getNumberOfRequiredParameters()) { |
203
|
|
|
$argumentsSuccessPass = false; |
204
|
|
|
} |
205
|
|
|
|
206
|
3 |
|
if ($argumentsSuccessPass && $functionReflection->isRunnable()) { |
207
|
3 |
|
array_walk( |
208
|
3 |
|
$arguments, |
209
|
3 |
|
function (&$item) { |
210
|
|
|
/** @var CompiledExpression $item */ |
211
|
3 |
|
$item = $item->getValue(); |
212
|
3 |
|
} |
213
|
3 |
|
); |
214
|
|
|
|
215
|
3 |
|
return new CompiledExpression( |
216
|
3 |
|
$functionReflection->getReturnType(), |
217
|
3 |
|
$functionReflection->run($arguments) |
218
|
3 |
|
); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
return new CompiledExpression($functionReflection->getReturnType()); |
222
|
|
|
} |
223
|
6 |
|
} |
224
|
|
|
|
225
|
6 |
|
if (!$exists) { |
226
|
|
|
$context->notice( |
227
|
|
|
'undefined-fcall', |
228
|
|
|
sprintf('Function %s() does not exist', $expr->name->parts[0]), |
229
|
|
|
$expr |
230
|
|
|
); |
231
|
|
|
} |
232
|
|
|
|
233
|
6 |
|
return new CompiledExpression(); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* @param \PhpParser\Node\Expr\FuncCall $expr |
238
|
|
|
* @return CompiledExpression[] |
239
|
|
|
*/ |
240
|
7 |
|
protected function parseArgs($expr, Context $context) |
241
|
|
|
{ |
242
|
7 |
|
$arguments = array(); |
243
|
|
|
|
244
|
7 |
|
foreach ($expr->args as $argument) { |
245
|
6 |
|
$arguments[] = $context->getExpressionCompiler()->compile($argument->value); |
246
|
7 |
|
} |
247
|
|
|
|
248
|
7 |
|
return $arguments; |
249
|
|
|
} |
250
|
|
|
} |
251
|
|
|
|
A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.
You can also find more information in the “Code” section of your repository.