annotate vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php @ 0:c75dbcec494b

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