fromStringInContext() public static method

public static fromStringInContext ( string $string, Context $context ) : Type
$string string A string representing a type
$context Context The context in which the type string was found
return Type Parse a type from the given string
Example #1
0
 /**
  * @param string $type_string
  * A '|' delimited string representing a type in the form
  * 'int|string|null|ClassName'.
  *
  * @param Context $context
  * The context in which the type string was
  * found
  *
  * @return UnionType
  */
 public static function fromStringInContext(string $type_string, Context $context) : UnionType
 {
     if (empty($type_string)) {
         return new UnionType();
     }
     return new UnionType(array_map(function (string $type_name) use($context, $type_string) {
         assert($type_name !== '', "Type cannot be empty. Type '{$type_name}' given as part of the union type '{$type_string}' in {$context}.");
         return Type::fromStringInContext($type_name, $context);
     }, array_filter(array_map(function (string $type_name) {
         return trim($type_name);
     }, explode('|', $type_string)))));
 }
Example #2
0
 /**
  * Visit a node with kind `\ast\AST_NAME`
  *
  * @param Node $node
  * A node of the type indicated by the method name that we'd
  * like to figure out the type that it produces.
  *
  * @return UnionType
  * The set of types that are possibly produced by the
  * given node
  */
 public function visitName(Node $node) : UnionType
 {
     if ($node->flags & \ast\flags\NAME_NOT_FQ) {
         if ('parent' === $node->children['name']) {
             $class = $this->context->getClassInScope($this->code_base);
             return Type::fromFullyQualifiedString((string) $class->getParentClassFQSEN())->asUnionType();
         }
         return Type::fromStringInContext($node->children['name'], $this->context)->asUnionType();
     }
     return Type::fromFullyQualifiedString('\\' . $node->children['name'])->asUnionType();
 }
Example #3
0
 /**
  * Visit a node with kind `\ast\AST_NAME`
  *
  * @param Node $node
  * A node of the type indicated by the method name that we'd
  * like to figure out the type that it produces.
  *
  * @return UnionType
  * The set of types that are possibly produced by the
  * given node
  */
 public function visitName(Node $node) : UnionType
 {
     if ($node->flags & \ast\flags\NAME_NOT_FQ) {
         if ('parent' === $node->children['name']) {
             $class = $this->context->getClassInScope($this->code_base);
             if ($class->hasParentClassFQSEN()) {
                 return Type::fromFullyQualifiedString((string) $class->getParentClassFQSEN())->asUnionType();
             } else {
                 if (!$class->isTrait()) {
                     $this->emitIssue(Issue::ParentlessClass, $node->lineno ?? 0, (string) $class->getFQSEN());
                 }
                 return new UnionType();
             }
         }
         return Type::fromStringInContext($node->children['name'], $this->context)->asUnionType();
     }
     return Type::fromFullyQualifiedString('\\' . $node->children['name'])->asUnionType();
 }
Example #4
0
File: Type.php Project: etsy/phan
 /**
  * @param string $string
  * A string representing a type
  *
  * @param Context $context
  * The context in which the type string was
  * found
  *
  * @return Type
  * Parse a type from the given string
  */
 public static function fromStringInContext(string $string, Context $context) : Type
 {
     assert($string !== '', "Type cannot be empty");
     // Extract the namespace, type and parameter type name list
     $tuple = self::typeStringComponents($string);
     $namespace = $tuple->_0;
     $type_name = $tuple->_1;
     $template_parameter_type_name_list = $tuple->_2;
     // Map the names of the types to actual types in the
     // template parameter type list
     $template_parameter_type_list = array_map(function (string $type_name) use($context) {
         return Type::fromStringInContext($type_name, $context)->asUnionType();
     }, $template_parameter_type_name_list);
     // @var bool
     // True if this type name if of the form 'C[]'
     $is_generic_array_type = self::isGenericArrayString($type_name);
     // If this is a generic array type, get the name of
     // the type of each element
     $non_generic_array_type_name = $type_name;
     if ($is_generic_array_type && false !== ($pos = strrpos($type_name, '[]'))) {
         $non_generic_array_type_name = substr($type_name, 0, $pos);
     }
     // Check to see if the type name is mapped via
     // a using clause.
     //
     // Gotta check this before checking for native types
     // because there are monsters out there that will
     // remap the names via things like `use \Foo\String`.
     $non_generic_partially_qualified_array_type_name = $non_generic_array_type_name;
     if ($namespace) {
         $non_generic_partially_qualified_array_type_name = $namespace . '\\' . $non_generic_partially_qualified_array_type_name;
     }
     if ($context->hasNamespaceMapFor(\ast\flags\USE_NORMAL, $non_generic_partially_qualified_array_type_name)) {
         $fqsen = $context->getNamespaceMapFor(\ast\flags\USE_NORMAL, $non_generic_partially_qualified_array_type_name);
         if ($is_generic_array_type) {
             return GenericArrayType::fromElementType(Type::make($fqsen->getNamespace(), $fqsen->getName(), $template_parameter_type_list));
         }
         return Type::make($fqsen->getNamespace(), $fqsen->getName(), $template_parameter_type_list);
     }
     // If this was a fully qualified type, we're all
     // set
     if (!empty($namespace) && 0 === strpos($namespace, '\\')) {
         return self::make($namespace, $type_name, $template_parameter_type_list);
     }
     if ($is_generic_array_type && self::isNativeTypeString($type_name)) {
         return self::fromInternalTypeName($type_name);
     } else {
         // Check to see if its a builtin type
         switch (strtolower(self::canonicalNameFromName($type_name))) {
             case 'array':
                 return \Phan\Language\Type\ArrayType::instance();
             case 'bool':
                 return \Phan\Language\Type\BoolType::instance();
             case 'callable':
                 return \Phan\Language\Type\CallableType::instance();
             case 'float':
                 return \Phan\Language\Type\FloatType::instance();
             case 'int':
                 return \Phan\Language\Type\IntType::instance();
             case 'mixed':
                 return \Phan\Language\Type\MixedType::instance();
             case 'null':
                 return \Phan\Language\Type\NullType::instance();
             case 'object':
                 return \Phan\Language\Type\ObjectType::instance();
             case 'resource':
                 return \Phan\Language\Type\ResourceType::instance();
             case 'string':
                 return \Phan\Language\Type\StringType::instance();
             case 'void':
                 return \Phan\Language\Type\VoidType::instance();
             case 'static':
                 return \Phan\Language\Type\StaticType::instance();
         }
     }
     // Things like `self[]` or `$this[]`
     if ($is_generic_array_type && self::isSelfTypeString($non_generic_array_type_name) && $context->isInClassScope()) {
         // Callers of this method should be checking on their own
         // to see if this type is a reference to 'parent' and
         // dealing with it there. We don't want to have this
         // method be dependent on the code base
         assert('parent' !== $non_generic_array_type_name, __METHOD__ . " does not know how to handle the type name 'parent'");
         return GenericArrayType::fromElementType(static::fromFullyQualifiedString((string) $context->getClassFQSEN()));
     }
     // If this is a type referencing the current class
     // in scope such as 'self' or 'static', return that.
     if (self::isSelfTypeString($type_name) && $context->isInClassScope()) {
         // Callers of this method should be checking on their own
         // to see if this type is a reference to 'parent' and
         // dealing with it there. We don't want to have this
         // method be dependent on the code base
         assert('parent' !== $type_name, __METHOD__ . " does not know how to handle the type name 'parent'");
         return static::fromFullyQualifiedString((string) $context->getClassFQSEN());
     }
     // Merge the current namespace with the given relative
     // namespace
     if (!empty($context->getNamespace()) && !empty($namespace)) {
         $namespace = $context->getNamespace() . '\\' . $namespace;
     } else {
         if (!empty($context->getNamespace())) {
             $namespace = $context->getNamespace();
         } else {
             $namespace = '\\' . $namespace;
         }
     }
     // Attach the context's namespace to the type name
     return self::make($namespace, $type_name, $template_parameter_type_list);
 }
Example #5
0
 /**
  * @param Context $context
  * The context in which the comment line appears
  *
  * @param string $line
  * An individual line of a comment
  *
  * @return Option<Type>
  * An optional type overriding the extended type of the class
  */
 private static function inheritsFromCommentLine(Context $context, string $line)
 {
     $match = [];
     if (preg_match('/@inherits\\s+(' . Type::type_regex . ')/', $line, $match)) {
         $type_string = $match[1];
         $type = new Some(Type::fromStringInContext($type_string, $context));
         return $type;
     }
     return new None();
 }
Example #6
0
 /**
  * @param CodeBase $code_base
  * The code base within which we're operating
  *
  * @param $context $context
  * The context of the parser at the node for which we'd
  * like to determine a type
  *
  * @param Node|mixed $node
  * The node for which we'd like to determine its type
  *
  * @return UnionType
  * The UnionType associated with the given node
  * in the given Context within the given CodeBase
  *
  * @throws IssueException
  * An exception is thrown if we can't find a class for
  * the given type
  */
 public static function unionTypeFromClassNode(CodeBase $code_base, Context $context, $node) : UnionType
 {
     // For simple nodes or very complicated nodes,
     // recurse
     if (!$node instanceof \ast\Node || $node->kind != \ast\AST_NAME) {
         return self::unionTypeFromNode($code_base, $context, $node);
     }
     $class_name = $node->children['name'];
     if ('parent' === $class_name) {
         if (!$context->isInClassScope()) {
             throw new IssueException(Issue::fromType(Issue::ContextNotObject)($context->getFile(), $node->lineno ?? 0, [$class_name]));
         }
         $class = $context->getClassInScope($code_base);
         if ($class->isTrait()) {
             throw new IssueException(Issue::fromType(Issue::TraitParentReference)($context->getFile(), $node->lineno ?? 0, [(string) $context->getClassFQSEN()]));
         }
         if (!$class->hasParentType()) {
             throw new IssueException(Issue::fromType(Issue::ParentlessClass)($context->getFile(), $node->lineno ?? 0, [(string) $context->getClassFQSEN()]));
         }
         $parent_class_fqsen = $class->getParentClassFQSEN();
         if (!$code_base->hasClassWithFQSEN($parent_class_fqsen)) {
             throw new IssueException(Issue::fromType(Issue::UndeclaredClass)($context->getFile(), $node->lineno ?? 0, [(string) $parent_class_fqsen]));
         } else {
             $parent_class = $code_base->getClassByFQSEN($parent_class_fqsen);
             return $parent_class->getUnionType();
         }
     }
     // We're going to convert the class reference to a type
     $type = null;
     // Check to see if the name is fully qualified
     if (!($node->flags & \ast\flags\NAME_NOT_FQ)) {
         if (0 !== strpos($class_name, '\\')) {
             $class_name = '\\' . $class_name;
         }
         $type = Type::fromFullyQualifiedString($class_name);
     } else {
         $type = Type::fromStringInContext($class_name, $context);
     }
     return $type->asUnionType();
 }
Example #7
0
 /**
  * @param string $type_string
  * A '|' delimited string representing a type in the form
  * 'int|string|null|ClassName'.
  *
  * @param Context $context
  * The context in which the type string was
  * found
  *
  * @return UnionType
  */
 public static function fromStringInContext(string $type_string, Context $context) : UnionType
 {
     if (empty($type_string)) {
         return new UnionType();
     }
     // If our scope has a generic type identifier defined on it
     // that matches the type string, return that UnionType.
     if ($context->getScope()->hasTemplateType($type_string)) {
         return $context->getScope()->getTemplateType($type_string)->asUnionType();
     }
     return new UnionType(array_map(function (string $type_name) use($context, $type_string) {
         assert($type_name !== '', "Type cannot be empty.");
         return Type::fromStringInContext($type_name, $context);
     }, array_filter(array_map(function (string $type_name) {
         return trim($type_name);
     }, explode('|', $type_string)))));
 }