public static fromFullyQualifiedString ( string $fully_qualified_string ) : |
||
$fully_qualified_string | string | A fully qualified type name |
return |
/** * @param string $fully_qualified_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 fromFullyQualifiedString(string $fully_qualified_string) : UnionType { if (empty($fully_qualified_string)) { return new UnionType(); } return new UnionType(array_map(function (string $type_name) { return Type::fromFullyQualifiedString($type_name); }, explode('|', $fully_qualified_string))); }
private function visitClassNode(Node $node) : UnionType { // Things of the form `new $class_name();` if ($node->kind == \ast\AST_VAR) { return new UnionType(); } // Anonymous class of form `new class { ... }` if ($node->kind == \ast\AST_CLASS && $node->flags & \ast\flags\CLASS_ANONYMOUS) { // Generate a stable name for the anonymous class $anonymous_class_name = (new ContextNode($this->code_base, $this->context, $node))->getUnqualifiedNameForAnonymousClass(); // Turn that into a fully qualified name $fqsen = FullyQualifiedClassName::fromStringInContext($anonymous_class_name, $this->context); // Turn that into a union type return Type::fromFullyQualifiedString((string) $fqsen)->asUnionType(); } // Things of the form `new $method->name()` if ($node->kind !== \ast\AST_NAME) { return new UnionType(); } // Get the name of the class $class_name = $node->children['name']; // If this is a straight-forward class name, recurse into the // class node and get its type if (!Type::isSelfTypeString($class_name)) { // TODO: does anyone else call this method? return self::unionTypeFromClassNode($this->code_base, $this->context, $node); } // This is a self-referential node if (!$this->context->isInClassScope()) { Log::err(Log::ESTATIC, "Cannot access {$class_name} when not in a class scope", $this->context->getFile(), $node->lineno); return new UnionType(); } // Reference to a parent class if ($class_name === 'parent') { $class = $this->context->getClassInScope($this->code_base); if (!$class->hasParentClassFQSEN()) { Log::err(Log::ESTATIC, "Reference to parent of parentless class {$class->getFQSEN()}", $this->context->getFile(), $node->lineno); return new UnionType(); } return Type::fromFullyQualifiedString((string) $class->getParentClassFQSEN())->asUnionType(); } return Type::fromFullyQualifiedString((string) $this->context->getClassFQSEN())->asUnionType(); }
/** * @param string $fully_qualified_string * A fully qualified type name * * @param Context $context * The context in which the type string was * found * * @return UnionType */ public static function fromFullyQualifiedString(string $fully_qualified_string) : Type { assert(!empty($fully_qualified_string), "Type cannot be empty"); if (0 !== strpos($fully_qualified_string, '\\')) { return self::fromInternalTypeName($fully_qualified_string); } $tuple = self::typeStringComponents($fully_qualified_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) { return Type::fromFullyQualifiedString($type_name)->asUnionType(); }, $template_parameter_type_name_list); if (0 !== strpos($namespace, '\\')) { $namespace = '\\' . $namespace; } assert(!empty($namespace) && !empty($type_name), "Type was not fully qualified"); return self::make($namespace, $type_name, $template_parameter_type_list); }
/** * @return Type * The type of this class */ public function asType() : Type { return Type::fromFullyQualifiedString((string) $this); }
/** * @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(); }
public function testGenericArrayTypeFromString() { $type = Type::fromFullyQualifiedString("int[][]"); $this->assertEquals($type->genericArrayElementType()->__toString(), "int[]"); }
/** * As per the Serializable interface * * @param string $serialized * A serialized UnionType * * @return void * * @see \Serializable */ public function unserialize($serialized) { $this->type_set = new Set(array_map(function (string $type_name) { return Type::fromFullyQualifiedString($type_name); }, explode('|', $serialized ?? ''))); }