Beispiel #1
0
 /**
  * Visit a node with kind `\ast\AST_CLASS`
  *
  * @param Node $node
  * A node to parse
  *
  * @return Context
  * A new or an unchanged context resulting from
  * parsing the node
  */
 public function visitClass(Decl $node) : Context
 {
     if ($node->flags & \ast\flags\CLASS_ANONYMOUS) {
         $class_name = (new ContextNode($this->code_base, $this->context, $node))->getUnqualifiedNameForAnonymousClass();
     } else {
         $class_name = (string) $node->name;
     }
     // This happens now and then and I have no idea
     // why.
     if (empty($class_name)) {
         return $this->context;
     }
     assert(!empty($class_name), "Class must have name in {$this->context}");
     $class_fqsen = FullyQualifiedClassName::fromStringInContext($class_name, $this->context);
     assert($class_fqsen instanceof FullyQualifiedClassName, "The class FQSEN must be a FullyQualifiedClassName");
     // Hunt for an available alternate ID if necessary
     $alternate_id = 0;
     while ($this->code_base->hasClassWithFQSEN($class_fqsen)) {
         $class_fqsen = $class_fqsen->withAlternateId(++$alternate_id);
     }
     // Build the class from what we know so far
     $class_context = $this->context->withLineNumberStart($node->lineno ?? 0)->withLineNumberEnd($node->endLineno ?? -1);
     $class = new Clazz($class_context, $class_name, $class_fqsen->asUnionType(), $node->flags ?? 0, $class_fqsen);
     // Set the scope of the class's context to be the
     // internal scope of the class
     $class_context = $class_context->withScope($class->getInternalScope());
     // Get a comment on the class declaration
     $comment = Comment::fromStringInContext($node->docComment ?? '', $this->context);
     // Add any template types parameterizing a generic class
     foreach ($comment->getTemplateTypeList() as $template_type) {
         $class->getInternalScope()->addTemplateType($template_type);
     }
     $class->setIsDeprecated($comment->isDeprecated());
     $class->setSuppressIssueList($comment->getSuppressIssueList());
     // Add the class to the code base as a globally
     // accessible object
     $this->code_base->addClass($class);
     // Look to see if we have a parent class
     if (!empty($node->children['extends'])) {
         $parent_class_name = $node->children['extends']->children['name'];
         // Check to see if the name isn't fully qualified
         if ($node->children['extends']->flags & \ast\flags\NAME_NOT_FQ) {
             if ($this->context->hasNamespaceMapFor(T_CLASS, $parent_class_name)) {
                 // Get a fully-qualified name
                 $parent_class_name = (string) $this->context->getNamespaceMapFor(T_CLASS, $parent_class_name);
             } else {
                 $parent_class_name = $this->context->getNamespace() . '\\' . $parent_class_name;
             }
         }
         // The name is fully qualified. Make sure it looks
         // like it is
         if (0 !== strpos($parent_class_name, '\\')) {
             $parent_class_name = '\\' . $parent_class_name;
         }
         $parent_fqsen = FullyQualifiedClassName::fromStringInContext($parent_class_name, $this->context);
         // Set the parent for the class
         $class->setParentType($parent_fqsen->asType());
     }
     // If the class explicitly sets its overriding extension type,
     // set that on the class
     $inherited_type_option = $comment->getInheritedTypeOption();
     if ($inherited_type_option->isDefined()) {
         $class->setParentType($inherited_type_option->get());
     }
     // Add any implemeneted interfaces
     if (!empty($node->children['implements'])) {
         $interface_list = (new ContextNode($this->code_base, $this->context, $node->children['implements']))->getQualifiedNameList();
         foreach ($interface_list as $name) {
             $class->addInterfaceClassFQSEN(FullyQualifiedClassName::fromFullyQualifiedString($name));
         }
     }
     return $class_context;
 }