Utility method to fetch reflection class instance by name

Supports:
'self' keyword
'parent' keyword
not-FQN class names

@param Node\Name $node Class name node

@return bool|\ReflectionClass

@throws ReflectionException
431717
private function fetchReflectionClass(Node\Name $node)
    {
        $className  = $node->toString();
        $isFQNClass = $node instanceof Node\Name\FullyQualified;
        if ($isFQNClass) {
            // check to see if the class is already loaded and is safe to use
            // PHP's ReflectionClass to determine if the class is user defined
            if (class_exists($className, false)) {
                $refClass = new \ReflectionClass($className);
                if (!$refClass->isUserDefined()) {
                    return $refClass;
                }
            }
            return new ReflectionClass($className);
        }

        if ('self' === $className) {
            if ($this->context instanceof \ReflectionClass) {
                return $this->context;
            } elseif (method_exists($this->context, 'getDeclaringClass')) {
                return $this->context->getDeclaringClass();
            }
        }

        if ('parent' === $className) {
            if ($this->context instanceof \ReflectionClass) {
                return $this->context->getParentClass();
            } elseif (method_exists($this->context, 'getDeclaringClass')) {
                return $this->context->getDeclaringClass()->getParentClass();
            }
        }

        if (method_exists($this->context, 'getFileName')) {
            /** @var ReflectionFileNamespace|null $fileNamespace */
            $fileName      = $this->context->getFileName();
            $namespaceName = $this->resolveScalarMagicConstNamespace();

            $fileNamespace = new ReflectionFileNamespace($fileName, $namespaceName);
            return $fileNamespace->getClass($className);
        }

        throw new ReflectionException("Can not resolve class $className");
    }
308375
private function resolveNamespace(Node $node)
    {
        try {
            return $this->namespaceFromNode($node);
        } catch (Exception $e) {
        }

        foreach ($node->getDescendantNodes() as $node) {
            try {
                return $this->namespaceFromNode($node);
            } catch (Exception $e) {
            }
        }

        return '';
    }
385658
private static function getContainingClassFqn(Node $node)
    {
        $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class);
        if ($classNode === null) {
            return null;
        }
        return (string)$classNode->getNamespacedName();
    }
929845
public static function fromNode(Node $node): self
    {
        return new self($node::schema()->getQName(), (string)$node->get('_id'));
    }
522631
public ClassNode getClassNode(final String name) {
        final ClassNode[] result = new ClassNode[]{null};
        PrimaryClassNodeOperation handler = new PrimaryClassNodeOperation() {
            public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
                if (classNode.getName().equals(name)) {
                    result[0] = classNode;
                }
            }
        };

        try {
            applyToPrimaryClassNodes(handler);
        } catch (CompilationFailedException e) {
            if (debug) e.printStackTrace();
        }
        return result[0];
    }
219598
public function getClass($name)
    {
        if ($this->hasClasses()) {
            foreach ($this->getClasses() as $class) {
                if ($class->getName() === $name) {
                    return $class;
                }
            }
        }
        return null;
    }
1190042
private function exprClass(Node $class_name) {
    $colon_node = new PartialNode();
    $this->mustMatch(T_DOUBLE_COLON, $colon_node);
    if ($this->currentType === T_STRING) {
      $class_constant = $this->mustMatchToken(T_STRING);
      if ($this->currentType === '(') {
        return $this->classMethodCall($class_name, $colon_node, $class_constant);
      }
      else {
        return $this->classConstant($class_name, $colon_node, $class_constant);
      }
    }
    elseif ($this->currentType === T_CLASS) {
      return $this->classNameScalar($class_name, $colon_node);
    }
    else {
      return $this->classVariable($class_name, $colon_node);
    }
  }
879514
private function getCallingInfo(Node $node)
    {
        return coroutine(function () use ($node) {
            $fqn = null;
            $callingNode = null;
            if ($node instanceof Node\DelimitedList\ArgumentExpressionList) {
                // Cursor is already inside a (
                $argumentExpressionList = $node;
                if ($node->parent instanceof Node\Expression\ObjectCreationExpression) {
                    // Constructing something
                    $callingNode = $node->parent->classTypeDesignator;
                    if (!$callingNode instanceof Node\QualifiedName) {
                        // We only support constructing from a QualifiedName
                        return null;
                    }
                    $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($callingNode);
                    $fqn = "{$fqn}->__construct()";
                } else {
                    $callingNode = $node->parent->getFirstChildNode(
                        Node\Expression\MemberAccessExpression::class,
                        Node\Expression\ScopedPropertyAccessExpression::class,
                        Node\QualifiedName::class
                    );
                }
            } elseif ($node instanceof Node\Expression\CallExpression) {
                $argumentExpressionList = $node->getFirstChildNode(Node\DelimitedList\ArgumentExpressionList::class);
                $callingNode = $node->getFirstChildNode(
                    Node\Expression\MemberAccessExpression::class,
                    Node\Expression\ScopedPropertyAccessExpression::class,
                    Node\QualifiedName::class
                );
            } elseif ($node instanceof Node\Expression\ObjectCreationExpression) {
                $argumentExpressionList = $node->getFirstChildNode(Node\DelimitedList\ArgumentExpressionList::class);
                $callingNode = $node->classTypeDesignator;
                if (!$callingNode instanceof Node\QualifiedName) {
                    // We only support constructing from a QualifiedName
                    return null;
                }
                // Manually build the __construct fqn
                $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($callingNode);
                $fqn = "{$fqn}->__construct()";
            }

            if (!$callingNode) {
                return null;
            }

            // Now find the definition of the call
            $fqn = $fqn ?: DefinitionResolver::getDefinedFqn($callingNode);
            while (true) {
                if ($fqn) {
                    $def = $this->index->getDefinition($fqn);
                } else {
                    $def = $this->definitionResolver->resolveReferenceNodeToDefinition($callingNode);
                }
                if ($def !== null || $this->index->isComplete()) {
                    break;
                }
                yield waitForEvent($this->index, 'definition-added');
            }

            if (!$def) {
                return null;
            }

            return [$def, $argumentExpressionList];
        });
    }
736879
public function getQueryNames(AbstractNode $node, ReflectionClass $reflectionNodeClass): array
    {
        $queryNames = [];

        $queryNodes = $this->getQueryNodes($node);

        foreach ($queryNodes as $queryNode) {
            $queryName = $queryNode->getName();

            if (!$this->isQueryMethod($queryName) || $reflectionNodeClass->hasMethod($queryName)) {
                continue;
            }

            $queryNames[] = $queryName;
        }

        return array_unique($queryNames);
    }
1152729
public function get($name)
	{
		list($class, $name) = explode('::$', $name, 2);

		$class = new \ReflectionClass($class);
		$property = $class->getProperty($name);
		$property->setAccessible(TRUE);

		return $property->getValue();
	}
603684
protected final function getReflection(string $className): ReflectionClass
   {
       try {
           $reflexion = new ReflectionClass($className);
       } catch (ReflectionException $e) {
           throw new NotFoundException(
               "Can't get reflection for " . $className . ": not found");
       }
       if (!$reflexion->isInstantiable()) {
           throw new ContainerException(
               "Can't get reflection for " . $className . ": not instanciable");
       }
       return $reflexion;
   }
27995
public function enterNode(Node $node)
    {
        if ($node instanceof Class_ || $node instanceof Trait_) {
            $this->inClass = true;
        } elseif ($node instanceof FuncCall && !$this->inClass) {
            // We'll give any args at all (besides null) a pass.
            // Technically we should be checking whether the args are objects, but this will do for now.
            //
            // @todo switch this to actually validate args when we get context-aware code cleaner passes.
            if (!empty($node->args) && !$this->isNull($node->args[0])) {
                return;
            }

            // We'll ignore name expressions as well (things like `$foo()`)
            if (!($node->name instanceof Name)) {
                return;
            }

            $name = \strtolower($node->name);
            if (\in_array($name, ['get_class', 'get_called_class'])) {
                $msg = \sprintf('%s() called without object from outside a class', $name);
                throw new ErrorException($msg, 0, E_USER_WARNING, null, $node->getLine());
            }
        }
    }
759172
private function getSupportedReflector($node)
    {
        foreach ($this->delegateReflectors as $reflector) {
            if ($reflector->supports($node)) {
                return $reflector;
            }
        }

        return;
    }
1319728
private function resolveType($node)
    {
        if ($node instanceof Node\NullableType) {
            $node->type = $this->resolveType($node->type);
            return $node;
        }
        if ($node instanceof Name) {
            return $this->resolveClassName($node);
        }

        return $node;
    }
762181
protected function resolveClassName(Name $name): Name
    {
        // Skip all names except special `self`
        if (strtolower($name->toString()) !== 'self') {
            return $name;
        }

        // Save the original name
        $originalName = $name;
        $name = clone $originalName;
        $name->setAttribute('originalName', $originalName);

        $fullClassName    = Name::concat($this->namespace, $this->className);
        $resolvedSelfName = new FullyQualified('\\' . ltrim($fullClassName->toString(), '\\'), $name->getAttributes());

        $this->replacedNodes[] = $resolvedSelfName;

        return $resolvedSelfName;
    }
338963
public static function getDefinedFqn($node)
    {
        $parent = $node->parent;
        // Anonymous classes don't count as a definition
        // INPUT                    OUTPUT:
        // namespace A\B;
        // class C { }              A\B\C
        // interface C { }          A\B\C
        // trait C { }              A\B\C
        if (
            $node instanceof PhpParser\ClassLike
        ) {
            $className = (string)$node->getNamespacedName();
            // An (invalid) class declaration without a name will have an empty string as name,
            // but should not define an FQN
            if ($className === '') {
                return null;
            }
            return $className;
        }

        // INPUT                   OUTPUT:
        // namespace A\B;           A\B
        if ($node instanceof Node\Statement\NamespaceDefinition && $node->name instanceof Node\QualifiedName) {
            $name = (string) PhpParser\ResolvedName::buildName($node->name->nameParts, $node->getFileContents());
            return $name === "" ? null : $name;
        }

        // INPUT                   OUTPUT:
        // namespace A\B;
        // function a();           A\B\a();
        if ($node instanceof Node\Statement\FunctionDeclaration) {
            // Function: use functionName() as the name
            $name = (string)$node->getNamespacedName();
            return $name === "" ? null : $name . '()';
        }

        // INPUT                        OUTPUT
        // namespace A\B;
        // class C {
        //   function a () {}           A\B\C->a()
        //   static function b() {}     A\B\C::b()
        // }
        if ($node instanceof Node\MethodDeclaration) {
            // Class method: use ClassName->methodName() as name
            $class = $node->getFirstAncestor(
                Node\Expression\ObjectCreationExpression::class,
                PhpParser\ClassLike::class
            );
            if (!isset($class->name)) {
                // Ignore anonymous classes
                return null;
            }
            if ($node->isStatic()) {
                return (string)$class->getNamespacedName() . '::' . $node->getName() . '()';
            } else {
                return (string)$class->getNamespacedName() . '->' . $node->getName() . '()';
            }
        }

        // INPUT                        OUTPUT
        // namespace A\B;
        // class C {
        //   static $a = 4, $b = 4      A\B\C::$a, A\B\C::$b
        //   $a = 4, $b = 4             A\B\C->$a, A\B\C->$b // TODO verify variable name
        // }
        if (
            ($propertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node)) !== null &&
            ($classDeclaration =
                $node->getFirstAncestor(
                    Node\Expression\ObjectCreationExpression::class,
                    PhpParser\ClassLike::class
                )
            ) !== null && isset($classDeclaration->name)) {
            $name = $node->getName();
            if ($propertyDeclaration->isStatic()) {
                // Static Property: use ClassName::$propertyName as name
                return (string)$classDeclaration->getNamespacedName() . '::$' . $name;
            }

            // Instance Property: use ClassName->propertyName as name
            return (string)$classDeclaration->getNamespacedName() . '->' . $name;
        }

        // INPUT                        OUTPUT
        // namespace A\B;
        // const FOO = 5;               A\B\FOO
        // class C {
        //   const $a, $b = 4           A\B\C::$a(), A\B\C::$b
        // }
        if (($constDeclaration = ParserHelpers\tryGetConstOrClassConstDeclaration($node)) !== null) {
            if ($constDeclaration instanceof Node\Statement\ConstDeclaration) {
                // Basic constant: use CONSTANT_NAME as name
                return (string)$node->getNamespacedName();
            }

            // Class constant: use ClassName::CONSTANT_NAME as name
            $classDeclaration = $constDeclaration->getFirstAncestor(
                Node\Expression\ObjectCreationExpression::class,
                PhpParser\ClassLike::class
            );

            if (!isset($classDeclaration->name)) {
                return null;
            }
            return (string)$classDeclaration->getNamespacedName() . '::' . $node->getName();
        }

        if (ParserHelpers\isConstDefineExpression($node)) {
            return $node->argumentExpressionList->children[0]->expression->getStringContentsText();
        }

        return null;
    }
962266
public function load($name)
    {
        $helper = $this->getRegisteredHelper($name);

        $reflection = new \ReflectionClass($helper);
        $class = $reflection->newInstance($this->di);

        return $class;
    }