/** * Gets a type for a given name * * Checks for: * - Primitive types (string, int, double, boolean, resource) * - Array and map notations (array, string[], string*, [:string]) * - Any type (var) * - Void type (void) * - Function types * - Generic notations (util.collections.HashTable<lang.types.String, lang.Generic>) * - Anything else will be passed to XPClass::forName() * * @param string $type * @return lang.Type * @throws lang.IllegalStateException if type is empty */ public static function forName($type) { static $primitives = ['string' => 'string', 'int' => 'int', 'integer' => 'int', 'double' => 'double', 'float' => 'double', 'bool' => 'bool', 'boolean' => 'bool', 'HH\\int' => 'int', 'HH\\string' => 'string', 'HH\\float' => 'double', 'HH\\bool' => 'bool']; if (0 === strlen($type)) { throw new IllegalStateException('Empty type'); } // Map well-known primitives, var and void, handle rest syntactically: // * T[] is an array // * [:T] is a map // * T* is a vararg // * T<K, V> is a generic // * D<K, V> is a generic type definition D with K and V components // except if any of K, V contains a ?, in which case it's a wild // card type. // * T1|T2 is a type union // * Anything else is a qualified or unqualified class name if (isset($primitives[$type])) { return Primitive::forName($primitives[$type]); } else { if ('var' === $type || 'resource' === $type || 'HH\\mixed' === $type) { return self::$VAR; } else { if ('void' === $type || 'HH\\void' === $type || 'HH\\noreturn' === $type) { return self::$VOID; } else { if ('array' === $type) { return self::$ARRAY; } else { if ('object' === $type) { return self::$OBJECT; } else { if ('callable' === $type) { return self::$CALLABLE; } else { if ('iterable' === $type) { return self::$ITERABLE; } else { if (0 === substr_compare($type, 'function(', 0, 9)) { return FunctionType::forName($type); } else { if (0 === substr_compare($type, '[]', -2)) { return new ArrayType(self::forName(substr($type, 0, -2))); } else { if (0 === substr_compare($type, '[:', 0, 2)) { return new MapType(self::forName(substr($type, 2, -1))); } else { if ('?' === $type[0] || '@' === $type[0]) { return self::forName(substr($type, 1)); } else { if ('(' === $type[0]) { return self::forName(substr($type, 1, -1)); } else { if (0 === substr_compare($type, '*', -1)) { return new ArrayType(self::forName(substr($type, 0, -1))); } else { if (strstr($type, '|')) { return TypeUnion::forName($type); } else { if ('HH\\num' === $type) { return new TypeUnion([Primitive::$INT, Primitive::$DOUBLE]); } else { if ('HH\\arraykey' === $type) { return new TypeUnion([Primitive::$INT, Primitive::$STRING]); } else { if (false === ($p = strpos($type, '<'))) { return XPClass::forName($type); } else { if (strstr($type, '?')) { return WildcardType::forName($type); } else { $base = substr($type, 0, $p); $components = self::forNames(substr($type, $p + 1, -1)); if ('array' === $base) { return 1 === sizeof($components) ? new ArrayType($components[0]) : new MapType($components[1]); } else { return XPClass::forName($base)->newGenericType($components); } } } } } } } } } } } } } } } } } } } }
public function function_with_void_return_type() { $this->assertEquals(FunctionType::forName('function(): void'), typeof(eval('return function(): void { };'))); }
/** * Gets a type for a given name * * Checks for: * - Primitive types (string, int, double, boolean, resource) * - Array and map notations (array, string[], string*, [:string]) * - Any type (var) * - Void type (void) * - Function types * - Generic notations (util.collections.HashTable<lang.types.String, lang.Generic>) * - Anything else will be passed to XPClass::forName() * * @param string $type * @return lang.Type * @throws lang.IllegalStateException if type is empty */ public static function forName($type) { static $primitives = ['string' => 'string', 'int' => 'int', 'integer' => 'int', 'double' => 'double', 'float' => 'double', 'bool' => 'bool', 'boolean' => 'bool']; if (0 === strlen($type)) { throw new IllegalStateException('Empty type'); } // Map well-known primitives, var and void, handle rest syntactically: // * T[] is an array // * [:T] is a map // * T* is a vararg // * T<K, V> is a generic // * D<K, V> is a generic type definition D with K and V components // except if any of K, V contains a ?, in which case it's a wild // card type. // * T1|T2 is a type union // * Anything else is a qualified or unqualified class name if (isset($primitives[$type])) { return Primitive::forName($primitives[$type]); } else { if ('var' === $type || 'resource' === $type) { return self::$VAR; } else { if ('void' === $type) { return self::$VOID; } else { if ('array' === $type) { return self::$ARRAY; } else { if ('callable' === $type) { return self::$CALLABLE; } else { if (0 === substr_compare($type, 'function(', 0, 9)) { return FunctionType::forName($type); } else { if (0 === substr_compare($type, '[]', -2)) { return new ArrayType(self::forName(substr($type, 0, -2))); } else { if (0 === substr_compare($type, '[:', 0, 2)) { return new MapType(self::forName(substr($type, 2, -1))); } else { if ('(' === $type[0]) { return self::forName(substr($type, 1, -1)); } else { if (0 === substr_compare($type, '*', -1)) { return new ArrayType(self::forName(substr($type, 0, -1))); } else { if (strstr($type, '|')) { return TypeUnion::forName($type); } else { if (false === ($p = strpos($type, '<'))) { $normalized = strtr($type, '\\', '.'); return strstr($normalized, '.') ? XPClass::forName($normalized) : new XPClass($normalized); } else { if (strstr($type, '?')) { return WildcardType::forName($type); } else { $base = substr($type, 0, $p); $components = self::forNames(substr($type, $p + 1, -1)); return cast(self::forName($base), 'lang.XPClass')->newGenericType($components); } } } } } } } } } } } } } }
public function normal_and_var_arg_is_instance_of($type) { $this->assertTrue(FunctionType::forName($type)->isInstance(function (Type $t, ...$args) { })); }