1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Patsura Dmitry https://github.com/ovr <[email protected]> |
4
|
|
|
*/ |
5
|
|
|
|
6
|
|
|
namespace PHPSA\Definition; |
7
|
|
|
|
8
|
|
|
use PHPSA\CompiledExpression; |
9
|
|
|
use PHPSA\Compiler\SymbolTable; |
10
|
|
|
use PHPSA\Context; |
11
|
|
|
use PhpParser\Node; |
12
|
|
|
use PHPSA\Compiler\Parameter; |
13
|
|
|
use PHPSA\Compiler\Types; |
14
|
|
|
use PHPSA\Variable; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Closure Definition |
18
|
|
|
* |
19
|
|
|
* @package PHPSA\Definition |
20
|
|
|
*/ |
21
|
|
|
class ClosureDefinition extends ParentDefinition |
22
|
|
|
{ |
23
|
|
|
/** |
24
|
|
|
* @todo Use Finder |
25
|
|
|
* |
26
|
|
|
* @var string |
27
|
|
|
*/ |
28
|
|
|
protected $filepath; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var Node\Expr\Closure |
32
|
|
|
*/ |
33
|
|
|
protected $statement; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @var int |
37
|
|
|
*/ |
38
|
|
|
protected $returnTypes = CompiledExpression::MIXED; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var array |
42
|
|
|
*/ |
43
|
|
|
protected $possibleReturnTypes = []; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var SymbolTable |
47
|
|
|
*/ |
48
|
|
|
protected $symbolTable; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param Node\Expr\Closure $statement |
52
|
|
|
*/ |
53
|
|
|
public function __construct(Node\Expr\Closure $statement) |
54
|
|
|
{ |
55
|
|
|
$this->symbolTable = new SymbolTable(); |
56
|
|
|
$this->statement = $statement; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @param Context $context |
61
|
|
|
*/ |
62
|
|
|
public function preCompile(Context $context) |
63
|
|
|
{ |
64
|
|
|
if ($this->statement->uses) { |
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Store variables from User to next restore Context |
67
|
|
|
*/ |
68
|
|
|
foreach ($this->statement->uses as $variable) { |
69
|
|
|
$variable = $context->getSymbol($variable->var); |
|
|
|
|
70
|
|
|
if ($variable) { |
71
|
|
|
$variable->incGets(); |
72
|
|
|
|
73
|
|
|
$this->symbolTable->add(clone $variable); |
74
|
|
|
} |
75
|
|
|
} |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Compile function to check it |
81
|
|
|
* |
82
|
|
|
* @param Context $context |
83
|
|
|
* @return bool |
84
|
|
|
*/ |
85
|
|
|
public function compile(Context $context) |
|
|
|
|
86
|
|
|
{ |
87
|
|
|
if ($this->compiled) { |
88
|
|
|
return true; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
$context->setFilepath($this->filepath); |
92
|
|
|
$this->compiled = true; |
93
|
|
|
|
94
|
|
|
$context->clearSymbols(); |
95
|
|
|
|
96
|
|
|
// @todo rewrite when we will use symbol table in all places! |
97
|
|
|
foreach ($this->symbolTable->getVariables() as $variable) { |
98
|
|
|
$context->addVariable($variable); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
if ($context->scopePointer) { |
102
|
|
|
if ($context->scopePointer->isClassMethod()) { |
103
|
|
|
/** @var \PHPSA\Definition\ClassMethod $method */ |
104
|
|
|
$method = $context->scopePointer->getObject(); |
105
|
|
|
|
106
|
|
|
if (!$method->isStatic()) { |
107
|
|
|
$thisPtr = new Variable('this', $this, CompiledExpression::OBJECT); |
108
|
|
|
$thisPtr->incGets(); |
109
|
|
|
|
110
|
|
|
$this->symbolTable->add($thisPtr); |
111
|
|
|
$context->addVariable($thisPtr); |
112
|
|
|
} |
113
|
|
|
} elseif ($context->scopePointer->isClosure()) { |
114
|
|
|
/** @var \PHPSA\Definition\ClosureDefinition $closure */ |
115
|
|
|
$closure = $context->scopePointer->getObject(); |
116
|
|
|
|
117
|
|
|
$thisPtr = $closure->getSymbolTable()->get('this'); |
118
|
|
|
if ($thisPtr) { |
119
|
|
|
$thisPtr->incGets(); |
120
|
|
|
$context->addVariable($thisPtr); |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
$context->scopePointer = $this->getPointer(); |
126
|
|
|
$context->setScope(null); |
127
|
|
|
|
128
|
|
|
if (count($this->statement->stmts) == 0) { |
129
|
|
|
return $context->notice( |
130
|
|
|
'not-implemented-function', |
131
|
|
|
sprintf('Closure %s() is not implemented', $this->name), |
132
|
|
|
$this->statement |
133
|
|
|
); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
if (count($this->statement->params) > 0) { |
137
|
|
|
/** @var Node\Param $parameter */ |
138
|
|
|
foreach ($this->statement->params as $parameter) { |
139
|
|
|
$type = CompiledExpression::UNKNOWN; |
140
|
|
|
|
141
|
|
|
if ($parameter->type) { |
142
|
|
|
if (is_string($parameter->type)) { |
143
|
|
|
$type = Types::getType($parameter->type); |
144
|
|
|
} elseif ($parameter->type instanceof Node\Name) { |
145
|
|
|
$type = CompiledExpression::OBJECT; |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
$context->addVariable( |
150
|
|
|
new Parameter($parameter->name, null, $type, $parameter->byRef) |
151
|
|
|
); |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
foreach ($this->statement->stmts as $st) { |
156
|
|
|
\PHPSA\nodeVisitorFactory($st, $context); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
return true; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* @param CompiledExpression[] $arguments |
164
|
|
|
* @param Context $context |
165
|
|
|
* @return CompiledExpression |
166
|
|
|
*/ |
167
|
|
|
public function run(array $arguments, Context $context) |
|
|
|
|
168
|
|
|
{ |
169
|
|
|
return new CompiledExpression(); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* @return string |
174
|
|
|
*/ |
175
|
|
|
public function getFilepath() |
176
|
|
|
{ |
177
|
|
|
return $this->filepath; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* @param string $filepath |
182
|
|
|
*/ |
183
|
|
|
public function setFilepath($filepath) |
184
|
|
|
{ |
185
|
|
|
$this->filepath = $filepath; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @return string |
190
|
|
|
*/ |
191
|
|
|
public function getNamespace() |
192
|
|
|
{ |
193
|
|
|
return $this->namespace; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* @return SymbolTable |
198
|
|
|
*/ |
199
|
|
|
public function getSymbolTable() |
200
|
|
|
{ |
201
|
|
|
return $this->symbolTable; |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.