annotate vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@13 1 <?php declare(strict_types=1);
Chris@0 2
Chris@0 3 namespace PhpParser\NodeVisitor;
Chris@0 4
Chris@0 5 use PhpParser\ErrorHandler;
Chris@13 6 use PhpParser\NameContext;
Chris@0 7 use PhpParser\Node;
Chris@0 8 use PhpParser\Node\Expr;
Chris@0 9 use PhpParser\Node\Name;
Chris@0 10 use PhpParser\Node\Name\FullyQualified;
Chris@0 11 use PhpParser\Node\Stmt;
Chris@0 12 use PhpParser\NodeVisitorAbstract;
Chris@0 13
Chris@0 14 class NameResolver extends NodeVisitorAbstract
Chris@0 15 {
Chris@13 16 /** @var NameContext Naming context */
Chris@13 17 protected $nameContext;
Chris@0 18
Chris@0 19 /** @var bool Whether to preserve original names */
Chris@0 20 protected $preserveOriginalNames;
Chris@0 21
Chris@13 22 /** @var bool Whether to replace resolved nodes in place, or to add resolvedNode attributes */
Chris@13 23 protected $replaceNodes;
Chris@13 24
Chris@0 25 /**
Chris@0 26 * Constructs a name resolution visitor.
Chris@0 27 *
Chris@13 28 * Options:
Chris@13 29 * * preserveOriginalNames (default false): An "originalName" attribute will be added to
Chris@13 30 * all name nodes that underwent resolution.
Chris@13 31 * * replaceNodes (default true): Resolved names are replaced in-place. Otherwise, a
Chris@13 32 * resolvedName attribute is added. (Names that cannot be statically resolved receive a
Chris@13 33 * namespacedName attribute, as usual.)
Chris@0 34 *
Chris@0 35 * @param ErrorHandler|null $errorHandler Error handler
Chris@0 36 * @param array $options Options
Chris@0 37 */
Chris@0 38 public function __construct(ErrorHandler $errorHandler = null, array $options = []) {
Chris@13 39 $this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing);
Chris@13 40 $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false;
Chris@13 41 $this->replaceNodes = $options['replaceNodes'] ?? true;
Chris@13 42 }
Chris@13 43
Chris@13 44 /**
Chris@13 45 * Get name resolution context.
Chris@13 46 *
Chris@13 47 * @return NameContext
Chris@13 48 */
Chris@13 49 public function getNameContext() : NameContext {
Chris@13 50 return $this->nameContext;
Chris@0 51 }
Chris@0 52
Chris@0 53 public function beforeTraverse(array $nodes) {
Chris@13 54 $this->nameContext->startNamespace();
Chris@13 55 return null;
Chris@0 56 }
Chris@0 57
Chris@0 58 public function enterNode(Node $node) {
Chris@0 59 if ($node instanceof Stmt\Namespace_) {
Chris@13 60 $this->nameContext->startNamespace($node->name);
Chris@0 61 } elseif ($node instanceof Stmt\Use_) {
Chris@0 62 foreach ($node->uses as $use) {
Chris@0 63 $this->addAlias($use, $node->type, null);
Chris@0 64 }
Chris@0 65 } elseif ($node instanceof Stmt\GroupUse) {
Chris@0 66 foreach ($node->uses as $use) {
Chris@0 67 $this->addAlias($use, $node->type, $node->prefix);
Chris@0 68 }
Chris@0 69 } elseif ($node instanceof Stmt\Class_) {
Chris@0 70 if (null !== $node->extends) {
Chris@0 71 $node->extends = $this->resolveClassName($node->extends);
Chris@0 72 }
Chris@0 73
Chris@0 74 foreach ($node->implements as &$interface) {
Chris@0 75 $interface = $this->resolveClassName($interface);
Chris@0 76 }
Chris@0 77
Chris@0 78 if (null !== $node->name) {
Chris@0 79 $this->addNamespacedName($node);
Chris@0 80 }
Chris@0 81 } elseif ($node instanceof Stmt\Interface_) {
Chris@0 82 foreach ($node->extends as &$interface) {
Chris@0 83 $interface = $this->resolveClassName($interface);
Chris@0 84 }
Chris@0 85
Chris@0 86 $this->addNamespacedName($node);
Chris@0 87 } elseif ($node instanceof Stmt\Trait_) {
Chris@0 88 $this->addNamespacedName($node);
Chris@0 89 } elseif ($node instanceof Stmt\Function_) {
Chris@0 90 $this->addNamespacedName($node);
Chris@0 91 $this->resolveSignature($node);
Chris@0 92 } elseif ($node instanceof Stmt\ClassMethod
Chris@0 93 || $node instanceof Expr\Closure
Chris@0 94 ) {
Chris@0 95 $this->resolveSignature($node);
Chris@17 96 } elseif ($node instanceof Stmt\Property) {
Chris@17 97 if (null !== $node->type) {
Chris@17 98 $node->type = $this->resolveType($node->type);
Chris@17 99 }
Chris@0 100 } elseif ($node instanceof Stmt\Const_) {
Chris@0 101 foreach ($node->consts as $const) {
Chris@0 102 $this->addNamespacedName($const);
Chris@0 103 }
Chris@0 104 } elseif ($node instanceof Expr\StaticCall
Chris@0 105 || $node instanceof Expr\StaticPropertyFetch
Chris@0 106 || $node instanceof Expr\ClassConstFetch
Chris@0 107 || $node instanceof Expr\New_
Chris@0 108 || $node instanceof Expr\Instanceof_
Chris@0 109 ) {
Chris@0 110 if ($node->class instanceof Name) {
Chris@0 111 $node->class = $this->resolveClassName($node->class);
Chris@0 112 }
Chris@0 113 } elseif ($node instanceof Stmt\Catch_) {
Chris@0 114 foreach ($node->types as &$type) {
Chris@0 115 $type = $this->resolveClassName($type);
Chris@0 116 }
Chris@0 117 } elseif ($node instanceof Expr\FuncCall) {
Chris@0 118 if ($node->name instanceof Name) {
Chris@13 119 $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION);
Chris@0 120 }
Chris@0 121 } elseif ($node instanceof Expr\ConstFetch) {
Chris@13 122 $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT);
Chris@0 123 } elseif ($node instanceof Stmt\TraitUse) {
Chris@0 124 foreach ($node->traits as &$trait) {
Chris@0 125 $trait = $this->resolveClassName($trait);
Chris@0 126 }
Chris@0 127
Chris@0 128 foreach ($node->adaptations as $adaptation) {
Chris@0 129 if (null !== $adaptation->trait) {
Chris@0 130 $adaptation->trait = $this->resolveClassName($adaptation->trait);
Chris@0 131 }
Chris@0 132
Chris@0 133 if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) {
Chris@0 134 foreach ($adaptation->insteadof as &$insteadof) {
Chris@0 135 $insteadof = $this->resolveClassName($insteadof);
Chris@0 136 }
Chris@0 137 }
Chris@0 138 }
Chris@0 139 }
Chris@13 140
Chris@13 141 return null;
Chris@0 142 }
Chris@0 143
Chris@13 144 private function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) {
Chris@0 145 // Add prefix for group uses
Chris@0 146 $name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
Chris@0 147 // Type is determined either by individual element or whole use declaration
Chris@0 148 $type |= $use->type;
Chris@0 149
Chris@13 150 $this->nameContext->addAlias(
Chris@13 151 $name, (string) $use->getAlias(), $type, $use->getAttributes()
Chris@13 152 );
Chris@0 153 }
Chris@0 154
Chris@0 155 /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */
Chris@0 156 private function resolveSignature($node) {
Chris@0 157 foreach ($node->params as $param) {
Chris@0 158 $param->type = $this->resolveType($param->type);
Chris@0 159 }
Chris@0 160 $node->returnType = $this->resolveType($node->returnType);
Chris@0 161 }
Chris@0 162
Chris@0 163 private function resolveType($node) {
Chris@0 164 if ($node instanceof Node\NullableType) {
Chris@0 165 $node->type = $this->resolveType($node->type);
Chris@0 166 return $node;
Chris@0 167 }
Chris@0 168 if ($node instanceof Name) {
Chris@0 169 return $this->resolveClassName($node);
Chris@0 170 }
Chris@0 171 return $node;
Chris@0 172 }
Chris@0 173
Chris@13 174 /**
Chris@13 175 * Resolve name, according to name resolver options.
Chris@13 176 *
Chris@13 177 * @param Name $name Function or constant name to resolve
Chris@13 178 * @param int $type One of Stmt\Use_::TYPE_*
Chris@13 179 *
Chris@13 180 * @return Name Resolved name, or original name with attribute
Chris@13 181 */
Chris@13 182 protected function resolveName(Name $name, int $type) : Name {
Chris@13 183 if (!$this->replaceNodes) {
Chris@13 184 $resolvedName = $this->nameContext->getResolvedName($name, $type);
Chris@13 185 if (null !== $resolvedName) {
Chris@13 186 $name->setAttribute('resolvedName', $resolvedName);
Chris@13 187 } else {
Chris@13 188 $name->setAttribute('namespacedName', FullyQualified::concat(
Chris@13 189 $this->nameContext->getNamespace(), $name, $name->getAttributes()));
Chris@13 190 }
Chris@13 191 return $name;
Chris@13 192 }
Chris@13 193
Chris@0 194 if ($this->preserveOriginalNames) {
Chris@0 195 // Save the original name
Chris@0 196 $originalName = $name;
Chris@0 197 $name = clone $originalName;
Chris@0 198 $name->setAttribute('originalName', $originalName);
Chris@0 199 }
Chris@0 200
Chris@13 201 $resolvedName = $this->nameContext->getResolvedName($name, $type);
Chris@13 202 if (null !== $resolvedName) {
Chris@13 203 return $resolvedName;
Chris@0 204 }
Chris@0 205
Chris@13 206 // unqualified names inside a namespace cannot be resolved at compile-time
Chris@13 207 // add the namespaced version of the name as an attribute
Chris@13 208 $name->setAttribute('namespacedName', FullyQualified::concat(
Chris@13 209 $this->nameContext->getNamespace(), $name, $name->getAttributes()));
Chris@13 210 return $name;
Chris@0 211 }
Chris@0 212
Chris@13 213 protected function resolveClassName(Name $name) {
Chris@13 214 return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL);
Chris@0 215 }
Chris@0 216
Chris@0 217 protected function addNamespacedName(Node $node) {
Chris@13 218 $node->namespacedName = Name::concat(
Chris@13 219 $this->nameContext->getNamespace(), (string) $node->name);
Chris@0 220 }
Chris@0 221 }