This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @author Nikita Popov @nikic |
||
4 | * @link https://github.com/nikic/PHP-Parser/blob/master/lib/PhpParser/NodeVisitor/NameResolver.php |
||
5 | * |
||
6 | * @author Patsura Dmitry https://github.com/ovr <[email protected]> |
||
7 | */ |
||
8 | |||
9 | namespace PHPSA\Compiler; |
||
10 | |||
11 | use PhpParser\Error; |
||
12 | use PhpParser\ErrorHandler; |
||
13 | use PhpParser\Node; |
||
14 | use PhpParser\Node\Expr; |
||
15 | use PhpParser\Node\Name; |
||
16 | use PhpParser\Node\Name\FullyQualified; |
||
17 | use PhpParser\Node\Stmt; |
||
18 | use PhpParser\NodeVisitorAbstract; |
||
19 | |||
20 | class NameResolveVisitor extends NodeVisitorAbstract |
||
0 ignored issues
–
show
|
|||
21 | { |
||
22 | /** @var null|Name Current namespace */ |
||
23 | protected $namespace; |
||
24 | |||
25 | /** @var array Map of format [aliasType => [aliasName => originalName]] */ |
||
26 | protected $aliases; |
||
27 | |||
28 | /** @var ErrorHandler Error handler */ |
||
29 | protected $errorHandler; |
||
30 | |||
31 | /** |
||
32 | * Constructs a name resolution visitor. |
||
33 | * |
||
34 | * @param ErrorHandler|null $errorHandler Error handler |
||
35 | */ |
||
36 | 1 | public function __construct(ErrorHandler $errorHandler = null) |
|
37 | { |
||
38 | 1 | $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing; |
|
39 | 1 | } |
|
40 | |||
41 | /** |
||
42 | * {@inheritdoc} |
||
43 | */ |
||
44 | 1 | public function beforeTraverse(array $nodes) |
|
45 | { |
||
46 | 1 | $this->resetState(); |
|
47 | 1 | } |
|
48 | |||
49 | /** |
||
50 | * {@inheritdoc} |
||
51 | */ |
||
52 | 1 | public function enterNode(Node $node) |
|
53 | { |
||
54 | 1 | if ($node instanceof Stmt\Namespace_) { |
|
55 | 1 | $this->resetState($node->name); |
|
56 | 1 | } elseif ($node instanceof Stmt\Use_) { |
|
57 | foreach ($node->uses as $use) { |
||
58 | $this->addAlias($use, $node->type, null); |
||
59 | } |
||
60 | 1 | } elseif ($node instanceof Stmt\GroupUse) { |
|
61 | foreach ($node->uses as $use) { |
||
62 | $this->addAlias($use, $node->type, $node->prefix); |
||
63 | } |
||
64 | 1 | } elseif ($node instanceof Stmt\Class_) { |
|
65 | 1 | if (null !== $node->extends) { |
|
66 | $node->extends = $this->resolveClassName($node->extends); |
||
67 | } |
||
68 | |||
69 | 1 | foreach ($node->implements as &$interface) { |
|
70 | $interface = $this->resolveClassName($interface); |
||
71 | } |
||
72 | |||
73 | 1 | if (null !== $node->name) { |
|
74 | 1 | $this->addNamespacedName($node); |
|
75 | } |
||
76 | 1 | } elseif ($node instanceof Stmt\Interface_) { |
|
77 | foreach ($node->extends as &$interface) { |
||
0 ignored issues
–
show
The expression
$node->extends of type null|object<PhpParser\Node\Name> is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
78 | $interface = $this->resolveClassName($interface); |
||
79 | } |
||
80 | |||
81 | $this->addNamespacedName($node); |
||
82 | 1 | } elseif ($node instanceof Stmt\Trait_) { |
|
83 | $this->addNamespacedName($node); |
||
84 | 1 | } elseif ($node instanceof Stmt\Function_) { |
|
85 | $this->addNamespacedName($node); |
||
86 | $this->resolveSignature($node); |
||
87 | 1 | } elseif ($node instanceof Stmt\ClassMethod |
|
88 | 1 | || $node instanceof Expr\Closure |
|
89 | ) { |
||
90 | $this->resolveSignature($node); |
||
91 | 1 | } elseif ($node instanceof Stmt\Const_) { |
|
92 | foreach ($node->consts as $const) { |
||
93 | $this->addNamespacedName($const); |
||
94 | } |
||
95 | 1 | } elseif ($node instanceof Expr\StaticCall |
|
96 | 1 | || $node instanceof Expr\StaticPropertyFetch |
|
97 | 1 | || $node instanceof Expr\ClassConstFetch |
|
98 | 1 | || $node instanceof Expr\New_ |
|
99 | 1 | || $node instanceof Expr\Instanceof_ |
|
100 | ) { |
||
101 | if ($node->class instanceof Name) { |
||
102 | $node->class = $this->resolveClassName($node->class); |
||
103 | } |
||
104 | 1 | } elseif ($node instanceof Stmt\Catch_) { |
|
105 | foreach ($node->types as &$type) { |
||
106 | $type = $this->resolveClassName($type); |
||
107 | } |
||
108 | 1 | } elseif ($node instanceof Expr\FuncCall) { |
|
109 | if ($node->name instanceof Name) { |
||
110 | $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_FUNCTION); |
||
111 | } |
||
112 | 1 | } elseif ($node instanceof Expr\ConstFetch) { |
|
113 | $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_CONSTANT); |
||
0 ignored issues
–
show
It seems like
$node->name can be null ; however, resolveOtherName() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
114 | 1 | } elseif ($node instanceof Stmt\TraitUse) { |
|
115 | foreach ($node->traits as &$trait) { |
||
116 | $trait = $this->resolveClassName($trait); |
||
117 | } |
||
118 | |||
119 | foreach ($node->adaptations as $adaptation) { |
||
120 | if (null !== $adaptation->trait) { |
||
121 | $adaptation->trait = $this->resolveClassName($adaptation->trait); |
||
122 | } |
||
123 | |||
124 | if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) { |
||
125 | foreach ($adaptation->insteadof as &$insteadof) { |
||
126 | $insteadof = $this->resolveClassName($insteadof); |
||
127 | } |
||
128 | } |
||
129 | } |
||
130 | 1 | } elseif ($node instanceof Node\NullableType) { |
|
131 | if ($node->type instanceof Name) { |
||
132 | $node->type = $this->resolveClassName($node->type); |
||
133 | } |
||
134 | } |
||
135 | 1 | } |
|
136 | |||
137 | 1 | protected function resetState(Name $namespace = null) |
|
138 | { |
||
139 | 1 | $this->namespace = $namespace; |
|
140 | 1 | $this->aliases = array( |
|
141 | 1 | Stmt\Use_::TYPE_NORMAL => array(), |
|
142 | 1 | Stmt\Use_::TYPE_FUNCTION => array(), |
|
143 | 1 | Stmt\Use_::TYPE_CONSTANT => array(), |
|
144 | ); |
||
145 | 1 | } |
|
146 | |||
147 | protected function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) |
||
148 | { |
||
149 | // Add prefix for group uses |
||
150 | $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; |
||
151 | // Type is determined either by individual element or whole use declaration |
||
152 | $type |= $use->type; |
||
153 | |||
154 | // Constant names are case sensitive, everything else case insensitive |
||
155 | if ($type === Stmt\Use_::TYPE_CONSTANT) { |
||
156 | $aliasName = $use->alias; |
||
157 | } else { |
||
158 | $aliasName = strtolower($use->alias); |
||
159 | } |
||
160 | |||
161 | if (isset($this->aliases[$type][$aliasName])) { |
||
162 | $typeStringMap = array( |
||
163 | Stmt\Use_::TYPE_NORMAL => '', |
||
164 | Stmt\Use_::TYPE_FUNCTION => 'function ', |
||
165 | Stmt\Use_::TYPE_CONSTANT => 'const ', |
||
166 | ); |
||
167 | |||
168 | $this->errorHandler->handleError(new Error( |
||
169 | sprintf( |
||
170 | 'Cannot use %s%s as %s because the name is already in use', |
||
171 | $typeStringMap[$type], |
||
172 | $name, |
||
173 | $use->alias |
||
174 | ), |
||
175 | $use->getAttributes() |
||
176 | )); |
||
177 | return; |
||
178 | } |
||
179 | |||
180 | $this->aliases[$type][$aliasName] = $name; |
||
181 | } |
||
182 | |||
183 | /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */ |
||
184 | private function resolveSignature($node) |
||
185 | { |
||
186 | foreach ($node->params as $param) { |
||
187 | if ($param->type instanceof Name) { |
||
188 | $param->type = $this->resolveClassName($param->type); |
||
189 | } |
||
190 | } |
||
191 | |||
192 | if ($node->returnType instanceof Name) { |
||
193 | $node->returnType = $this->resolveClassName($node->returnType); |
||
194 | } |
||
195 | } |
||
196 | |||
197 | protected function resolveClassName(Name $name) |
||
198 | { |
||
199 | // don't resolve special class names |
||
200 | if (in_array(strtolower($name->toString()), array('self', 'parent', 'static'))) { |
||
201 | if (!$name->isUnqualified()) { |
||
202 | $this->errorHandler->handleError(new Error( |
||
203 | sprintf("'\\%s' is an invalid class name", $name->toString()), |
||
204 | $name->getAttributes() |
||
205 | )); |
||
206 | } |
||
207 | |||
208 | return $name; |
||
209 | } |
||
210 | |||
211 | // fully qualified names are already resolved |
||
212 | if ($name->isFullyQualified()) { |
||
213 | return $name; |
||
214 | } |
||
215 | |||
216 | $aliasName = strtolower($name->getFirst()); |
||
217 | if (!$name->isRelative() && isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName])) { |
||
218 | // resolve aliases (for non-relative names) |
||
219 | $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName]; |
||
220 | return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); |
||
221 | } |
||
222 | |||
223 | // if no alias exists prepend current namespace |
||
224 | return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); |
||
225 | } |
||
226 | |||
227 | protected function resolveOtherName(Name $name, $type) |
||
228 | { |
||
229 | // fully qualified names are already resolved |
||
230 | if ($name->isFullyQualified()) { |
||
231 | return $name; |
||
232 | } |
||
233 | |||
234 | // resolve aliases for qualified names |
||
235 | $aliasName = strtolower($name->getFirst()); |
||
236 | if ($name->isQualified() && isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName])) { |
||
237 | $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName]; |
||
238 | return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); |
||
239 | } |
||
240 | |||
241 | if ($name->isUnqualified()) { |
||
242 | if ($type === Stmt\Use_::TYPE_CONSTANT) { |
||
243 | // constant aliases are case-sensitive, function aliases case-insensitive |
||
244 | $aliasName = $name->getFirst(); |
||
245 | } |
||
246 | |||
247 | if (isset($this->aliases[$type][$aliasName])) { |
||
248 | // resolve unqualified aliases |
||
249 | return new FullyQualified($this->aliases[$type][$aliasName], $name->getAttributes()); |
||
250 | } |
||
251 | |||
252 | if (null === $this->namespace) { |
||
253 | // outside of a namespace unaliased unqualified is same as fully qualified |
||
254 | return new FullyQualified($name, $name->getAttributes()); |
||
255 | } |
||
256 | |||
257 | // unqualified names inside a namespace cannot be resolved at compile-time |
||
258 | // add the namespaced version of the name as an attribute |
||
259 | $name->setAttribute( |
||
260 | 'namespacedName', |
||
261 | FullyQualified::concat($this->namespace, $name, $name->getAttributes()) |
||
262 | ); |
||
263 | |||
264 | return $name; |
||
265 | } |
||
266 | |||
267 | // if no alias exists prepend current namespace |
||
268 | return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); |
||
269 | } |
||
270 | |||
271 | 1 | protected function addNamespacedName(Node $node) |
|
272 | { |
||
273 | 1 | $node->namespacedName = Name::concat($this->namespace, $node->name); |
|
0 ignored issues
–
show
Accessing
namespacedName on the interface PhpParser\Node suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
name on the interface PhpParser\Node suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
274 | $node->namespace = $this->namespace; |
||
0 ignored issues
–
show
Accessing
namespace on the interface PhpParser\Node suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
275 | } |
||
276 | } |
||
277 |
The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.
Some resources for further reading:
You can also find more detailed suggestions for refactoring in the “Code” section of your repository.