Phan\Language\Element\Method::fromNode PHP Method

fromNode() public static method

public static fromNode ( Context $context, CodeBase $code_base, ast\Node\Decl $node, FullyQualifiedMethodName $fqsen ) : Method
$context Phan\Language\Context The context in which the node appears
$code_base Phan\CodeBase
$node ast\Node\Decl An AST node representing a method
$fqsen Phan\Language\FQSEN\FullyQualifiedMethodName
return Method A Method representing the AST node in the given context
    public static function fromNode(Context $context, CodeBase $code_base, Decl $node, FullyQualifiedMethodName $fqsen) : Method
    {
        // Create the skeleton method object from what
        // we know so far
        $method = new Method($context, (string) $node->name, new UnionType(), $node->flags ?? 0, $fqsen);
        // Parse the comment above the method to get
        // extra meta information about the method.
        $comment = Comment::fromStringInContext($node->docComment ?? '', $context);
        // @var Parameter[]
        // The list of parameters specified on the
        // method
        $parameter_list = Parameter::listFromNode($context, $code_base, $node->children['params']);
        // Add each parameter to the scope of the function
        foreach ($parameter_list as $parameter) {
            $method->getInternalScope()->addVariable($parameter);
        }
        // If the method is Analyzable, set the node so that
        // we can come back to it whenever we like and
        // rescan it
        $method->setNode($node);
        // Set the parameter list on the method
        $method->setParameterList($parameter_list);
        $method->setNumberOfRequiredParameters(array_reduce($parameter_list, function (int $carry, Parameter $parameter) : int {
            return $carry + ($parameter->isRequired() ? 1 : 0);
        }, 0));
        $method->setNumberOfOptionalParameters(array_reduce($parameter_list, function (int $carry, Parameter $parameter) : int {
            return $carry + ($parameter->isOptional() ? 1 : 0);
        }, 0));
        // Check to see if the comment specifies that the
        // method is deprecated
        $method->setIsDeprecated($comment->isDeprecated());
        $method->setSuppressIssueList($comment->getSuppressIssueList());
        if ($method->getIsMagicCall() || $method->getIsMagicCallStatic()) {
            $method->setNumberOfOptionalParameters(999);
            $method->setNumberOfRequiredParameters(0);
        }
        // Take a look at method return types
        if ($node->children['returnType'] !== null) {
            // Get the type of the parameter
            $union_type = UnionType::fromNode($context, $code_base, $node->children['returnType']);
            $method->getUnionType()->addUnionType($union_type);
        }
        if ($comment->hasReturnUnionType()) {
            // See if we have a return type specified in the comment
            $union_type = $comment->getReturnType();
            if ($union_type->hasSelfType()) {
                // We can't actually figure out 'static' at this
                // point, but fill it in regardless. It will be partially
                // correct
                if ($context->isInClassScope()) {
                    // n.b.: We're leaving the reference to self, static
                    //       or $this in the type because I'm guessing
                    //       it doesn't really matter. Apologies if it
                    //       ends up being an issue.
                    $union_type->addUnionType($context->getClassFQSEN()->asUnionType());
                }
            }
            $method->getUnionType()->addUnionType($union_type);
        }
        // Add params to local scope for user functions
        if (!$method->isInternal()) {
            $parameter_offset = 0;
            foreach ($method->getParameterList() as $i => $parameter) {
                if ($parameter->getUnionType()->isEmpty()) {
                    // If there is no type specified in PHP, check
                    // for a docComment with @param declarations. We
                    // assume order in the docComment matches the
                    // parameter order in the code
                    if ($comment->hasParameterWithNameOrOffset($parameter->getName(), $parameter_offset)) {
                        $comment_type = $comment->getParameterWithNameOrOffset($parameter->getName(), $parameter_offset)->getUnionType();
                        $parameter->getUnionType()->addUnionType($comment_type);
                    }
                }
                // If there's a default value on the parameter, check to
                // see if the type of the default is cool with the
                // specified type.
                if ($parameter->hasDefaultValue()) {
                    $default_type = $parameter->getDefaultValueType();
                    if (!$default_type->isEqualTo(NullType::instance()->asUnionType())) {
                        if (!$default_type->isEqualTo(NullType::instance()->asUnionType()) && !$default_type->canCastToUnionType($parameter->getUnionType())) {
                            Issue::maybeEmit($code_base, $context, Issue::TypeMismatchDefault, $node->lineno ?? 0, (string) $parameter->getUnionType(), $parameter->getName(), (string) $default_type);
                        }
                    }
                    // If we have no other type info about a parameter,
                    // just because it has a default value of null
                    // doesn't mean that is its type. Any type can default
                    // to null
                    if ((string) $default_type === 'null' && !$parameter->getUnionType()->isEmpty()) {
                        $parameter->getUnionType()->addType(NullType::instance());
                    }
                }
                ++$parameter_offset;
            }
        }
        return $method;
    }

Usage Example

Beispiel #1
0
 /**
  * Visit a node with kind `\ast\AST_METHOD`
  *
  * @param Node $node
  * A node to parse
  *
  * @return Context
  * A new or an unchanged context resulting from
  * parsing the node
  */
 public function visitMethod(Decl $node) : Context
 {
     // Bomb out if we're not in a class context
     $class = $this->getContextClass();
     $method_name = (string) $node->name;
     $method_fqsen = FullyQualifiedMethodName::fromStringInContext($method_name, $this->context);
     // Hunt for an available alternate ID if necessary
     $alternate_id = 0;
     while ($this->code_base->hasMethodWithFQSEN($method_fqsen)) {
         $method_fqsen = $method_fqsen->withAlternateId(++$alternate_id);
     }
     $method = Method::fromNode(clone $this->context, $this->code_base, $node, $method_fqsen);
     $class->addMethod($this->code_base, $method, new None());
     if ('__construct' === $method_name) {
         $class->setIsParentConstructorCalled(false);
         if ($class->isGeneric()) {
             // Get the set of template type identifiers defined on
             // the class
             $template_type_identifiers = array_keys($class->getTemplateTypeMap());
             // Get the set of template type identifiers defined
             // across all parameter types
             $parameter_template_type_identifiers = [];
             foreach ($method->getParameterList() as $parameter) {
                 foreach ($parameter->getUnionType()->getTypeSet() as $type) {
                     if ($type instanceof TemplateType) {
                         $parameter_template_type_identifiers[] = $type->getName();
                     }
                 }
             }
             $missing_template_type_identifiers = array_diff($template_type_identifiers, $parameter_template_type_identifiers);
             if ($missing_template_type_identifiers) {
                 $this->emitIssue(Issue::GenericConstructorTypes, $node->lineno ?? 0, implode(',', $missing_template_type_identifiers), (string) $class->getFQSEN());
             }
         }
     } elseif ('__invoke' === $method_name) {
         $class->getUnionType()->addType(CallableType::instance());
     } elseif ('__toString' === $method_name && !$this->context->getIsStrictTypes()) {
         $class->getUnionType()->addType(StringType::instance());
     }
     // Create a new context with a new scope
     return $this->context->withScope($method->getInternalScope());
 }
All Usage Examples Of Phan\Language\Element\Method::fromNode