nikic /
PHP-Parser
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 | namespace PhpParser\NodeVisitor; |
||
| 4 | |||
| 5 | use PhpParser\NodeVisitorAbstract; |
||
| 6 | use PhpParser\Error; |
||
| 7 | use PhpParser\Node; |
||
| 8 | use PhpParser\Node\Name; |
||
| 9 | use PhpParser\Node\Name\FullyQualified; |
||
| 10 | use PhpParser\Node\Expr; |
||
| 11 | use PhpParser\Node\Stmt; |
||
| 12 | |||
| 13 | class NameResolver extends NodeVisitorAbstract |
||
| 14 | { |
||
| 15 | /** @var null|Name Current namespace */ |
||
| 16 | protected $namespace; |
||
| 17 | |||
| 18 | /** @var array Map of format [aliasType => [aliasName => originalName]] */ |
||
| 19 | protected $aliases; |
||
| 20 | |||
| 21 | public function beforeTraverse(array $nodes) { |
||
| 22 | $this->resetState(); |
||
| 23 | } |
||
| 24 | |||
| 25 | public function enterNode(Node $node) { |
||
| 26 | if ($node instanceof Stmt\Namespace_) { |
||
| 27 | $this->resetState($node->name); |
||
|
0 ignored issues
–
show
|
|||
| 28 | } elseif ($node instanceof Stmt\Use_) { |
||
| 29 | foreach ($node->uses as $use) { |
||
| 30 | $this->addAlias($use, $node->type, null); |
||
| 31 | } |
||
| 32 | } elseif ($node instanceof Stmt\GroupUse) { |
||
| 33 | foreach ($node->uses as $use) { |
||
| 34 | $this->addAlias($use, $node->type, $node->prefix); |
||
| 35 | } |
||
| 36 | } elseif ($node instanceof Stmt\Class_) { |
||
| 37 | if (null !== $node->extends) { |
||
| 38 | $node->extends = $this->resolveClassName($node->extends); |
||
| 39 | } |
||
| 40 | |||
| 41 | foreach ($node->implements as &$interface) { |
||
| 42 | $interface = $this->resolveClassName($interface); |
||
| 43 | } |
||
| 44 | |||
| 45 | if (null !== $node->name) { |
||
| 46 | $this->addNamespacedName($node); |
||
| 47 | } |
||
| 48 | } elseif ($node instanceof Stmt\Interface_) { |
||
| 49 | 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.
Loading history...
|
|||
| 50 | $interface = $this->resolveClassName($interface); |
||
| 51 | } |
||
| 52 | |||
| 53 | $this->addNamespacedName($node); |
||
| 54 | } elseif ($node instanceof Stmt\Trait_) { |
||
| 55 | $this->addNamespacedName($node); |
||
| 56 | } elseif ($node instanceof Stmt\Function_) { |
||
| 57 | $this->addNamespacedName($node); |
||
| 58 | $this->resolveSignature($node); |
||
| 59 | } elseif ($node instanceof Stmt\ClassMethod |
||
| 60 | || $node instanceof Expr\Closure |
||
| 61 | ) { |
||
| 62 | $this->resolveSignature($node); |
||
| 63 | } elseif ($node instanceof Stmt\Const_) { |
||
| 64 | foreach ($node->consts as $const) { |
||
| 65 | $this->addNamespacedName($const); |
||
| 66 | } |
||
| 67 | } elseif ($node instanceof Expr\StaticCall |
||
| 68 | || $node instanceof Expr\StaticPropertyFetch |
||
| 69 | || $node instanceof Expr\ClassConstFetch |
||
| 70 | || $node instanceof Expr\New_ |
||
| 71 | || $node instanceof Expr\Instanceof_ |
||
| 72 | ) { |
||
| 73 | if ($node->class instanceof Name) { |
||
| 74 | $node->class = $this->resolveClassName($node->class); |
||
| 75 | } |
||
| 76 | } elseif ($node instanceof Stmt\Catch_) { |
||
| 77 | $node->type = $this->resolveClassName($node->type); |
||
| 78 | } elseif ($node instanceof Expr\FuncCall) { |
||
| 79 | if ($node->name instanceof Name) { |
||
| 80 | $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_FUNCTION); |
||
| 81 | } |
||
| 82 | } elseif ($node instanceof Expr\ConstFetch) { |
||
| 83 | $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_CONSTANT); |
||
|
0 ignored issues
–
show
$node->name is of type string, but the function expects a object<PhpParser\Node\Name>.
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
Loading history...
|
|||
| 84 | } elseif ($node instanceof Stmt\TraitUse) { |
||
| 85 | foreach ($node->traits as &$trait) { |
||
| 86 | $trait = $this->resolveClassName($trait); |
||
| 87 | } |
||
| 88 | |||
| 89 | foreach ($node->adaptations as $adaptation) { |
||
| 90 | if (null !== $adaptation->trait) { |
||
| 91 | $adaptation->trait = $this->resolveClassName($adaptation->trait); |
||
| 92 | } |
||
| 93 | |||
| 94 | if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) { |
||
| 95 | foreach ($adaptation->insteadof as &$insteadof) { |
||
| 96 | $insteadof = $this->resolveClassName($insteadof); |
||
| 97 | } |
||
| 98 | } |
||
| 99 | } |
||
| 100 | |||
| 101 | } |
||
| 102 | } |
||
| 103 | |||
| 104 | protected function resetState(Name $namespace = null) { |
||
| 105 | $this->namespace = $namespace; |
||
| 106 | $this->aliases = array( |
||
| 107 | Stmt\Use_::TYPE_NORMAL => array(), |
||
| 108 | Stmt\Use_::TYPE_FUNCTION => array(), |
||
| 109 | Stmt\Use_::TYPE_CONSTANT => array(), |
||
| 110 | ); |
||
| 111 | } |
||
| 112 | |||
| 113 | protected function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) { |
||
| 114 | // Add prefix for group uses |
||
| 115 | $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; |
||
| 116 | // Type is determined either by individual element or whole use declaration |
||
| 117 | $type |= $use->type; |
||
| 118 | |||
| 119 | // Constant names are case sensitive, everything else case insensitive |
||
| 120 | if ($type === Stmt\Use_::TYPE_CONSTANT) { |
||
| 121 | $aliasName = $use->alias; |
||
| 122 | } else { |
||
| 123 | $aliasName = strtolower($use->alias); |
||
| 124 | } |
||
| 125 | |||
| 126 | if (isset($this->aliases[$type][$aliasName])) { |
||
| 127 | $typeStringMap = array( |
||
| 128 | Stmt\Use_::TYPE_NORMAL => '', |
||
| 129 | Stmt\Use_::TYPE_FUNCTION => 'function ', |
||
| 130 | Stmt\Use_::TYPE_CONSTANT => 'const ', |
||
| 131 | ); |
||
| 132 | |||
| 133 | throw new Error( |
||
| 134 | sprintf( |
||
| 135 | 'Cannot use %s%s as %s because the name is already in use', |
||
| 136 | $typeStringMap[$type], $name, $use->alias |
||
| 137 | ), |
||
| 138 | $use->getLine() |
||
| 139 | ); |
||
| 140 | } |
||
| 141 | |||
| 142 | $this->aliases[$type][$aliasName] = $name; |
||
| 143 | } |
||
| 144 | |||
| 145 | /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */ |
||
| 146 | private function resolveSignature($node) { |
||
| 147 | foreach ($node->params as $param) { |
||
| 148 | if ($param->type instanceof Name) { |
||
| 149 | $param->type = $this->resolveClassName($param->type); |
||
| 150 | } |
||
| 151 | } |
||
| 152 | if ($node->returnType instanceof Name) { |
||
| 153 | $node->returnType = $this->resolveClassName($node->returnType); |
||
| 154 | } |
||
| 155 | } |
||
| 156 | |||
| 157 | protected function resolveClassName(Name $name) { |
||
| 158 | // don't resolve special class names |
||
| 159 | if (in_array(strtolower($name->toString()), array('self', 'parent', 'static'))) { |
||
| 160 | if (!$name->isUnqualified()) { |
||
| 161 | throw new Error( |
||
| 162 | sprintf("'\\%s' is an invalid class name", $name->toString()), |
||
| 163 | $name->getLine() |
||
| 164 | ); |
||
| 165 | } |
||
| 166 | |||
| 167 | return $name; |
||
| 168 | } |
||
| 169 | |||
| 170 | // fully qualified names are already resolved |
||
| 171 | if ($name->isFullyQualified()) { |
||
| 172 | return $name; |
||
| 173 | } |
||
| 174 | |||
| 175 | $aliasName = strtolower($name->getFirst()); |
||
| 176 | View Code Duplication | if (!$name->isRelative() && isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName])) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 177 | // resolve aliases (for non-relative names) |
||
| 178 | $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName]; |
||
| 179 | return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); |
||
| 180 | } |
||
| 181 | |||
| 182 | if (null !== $this->namespace) { |
||
| 183 | // if no alias exists prepend current namespace |
||
| 184 | return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); |
||
| 185 | } |
||
| 186 | |||
| 187 | return new FullyQualified($name->parts, $name->getAttributes()); |
||
| 188 | } |
||
| 189 | |||
| 190 | protected function resolveOtherName(Name $name, $type) { |
||
| 191 | // fully qualified names are already resolved |
||
| 192 | if ($name->isFullyQualified()) { |
||
| 193 | return $name; |
||
| 194 | } |
||
| 195 | |||
| 196 | // resolve aliases for qualified names |
||
| 197 | $aliasName = strtolower($name->getFirst()); |
||
| 198 | View Code Duplication | if ($name->isQualified() && isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName])) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 199 | $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName]; |
||
| 200 | return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); |
||
| 201 | } |
||
| 202 | |||
| 203 | if ($name->isUnqualified()) { |
||
| 204 | if ($type === Stmt\Use_::TYPE_CONSTANT) { |
||
| 205 | // constant aliases are case-sensitive, function aliases case-insensitive |
||
| 206 | $aliasName = $name->getFirst(); |
||
| 207 | } |
||
| 208 | |||
| 209 | if (!isset($this->aliases[$type][$aliasName])) { |
||
| 210 | // unqualified, unaliased names cannot be resolved at compile-time |
||
| 211 | return $name; |
||
| 212 | } |
||
| 213 | |||
| 214 | // resolve unqualified aliases |
||
| 215 | return new FullyQualified($this->aliases[$type][$aliasName], $name->getAttributes()); |
||
| 216 | } |
||
| 217 | |||
| 218 | if (null !== $this->namespace) { |
||
| 219 | // if no alias exists prepend current namespace |
||
| 220 | return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); |
||
| 221 | } |
||
| 222 | |||
| 223 | return new FullyQualified($name->parts, $name->getAttributes()); |
||
| 224 | } |
||
| 225 | |||
| 226 | protected function addNamespacedName(Node $node) { |
||
| 227 | if (null !== $this->namespace) { |
||
| 228 | $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
Loading history...
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
Loading history...
|
|||
| 229 | } else { |
||
| 230 | $node->namespacedName = new Name($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
Loading history...
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
Loading history...
|
|||
| 231 | } |
||
| 232 | } |
||
| 233 | } |
||
| 234 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: