*/
private $types;
/**
* @var bool
*/
private $simple;
/**
* @var string
*/
private $kind;
// | &
/**
* Creates a Type object based on reflection. Resolves self, static and parent to the actual class name.
* If the subject has no type, it returns null.
* @param \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection
*/
public static function fromReflection($reflection) : ?self
{
$type = $reflection instanceof \ReflectionFunctionAbstract ? $reflection->getReturnType() ?? (\PHP_VERSION_ID >= 80100 && $reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null) : $reflection->getType();
return $type ? self::fromReflectionType($type, $reflection, \true) : null;
}
/**
* @return $this|string
*/
private static function fromReflectionType(\ReflectionType $type, $of, bool $asObject)
{
if ($type instanceof \ReflectionNamedType) {
$name = self::resolve($type->getName(), $of);
return $asObject ? new self($type->allowsNull() && $name !== 'mixed' ? [$name, 'null'] : [$name]) : $name;
} elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) {
return new self(\array_map(function ($t) use($of) {
return self::fromReflectionType($t, $of, \false);
}, $type->getTypes()), $type instanceof \ReflectionUnionType ? '|' : '&');
} else {
throw new Nette\InvalidStateException('Unexpected type of ' . Reflection::toString($of));
}
}
/**
* Creates the Type object according to the text notation.
*/
public static function fromString(string $type) : self
{
if (!Validators::isTypeDeclaration($type)) {
throw new Nette\InvalidArgumentException("Invalid type '{$type}'.");
}
if ($type[0] === '?') {
return new self([\substr($type, 1), 'null']);
}
$unions = [];
foreach (\explode('|', $type) as $part) {
$part = \explode('&', \trim($part, '()'));
$unions[] = \count($part) === 1 ? $part[0] : new self($part, '&');
}
return \count($unions) === 1 && $unions[0] instanceof self ? $unions[0] : new self($unions);
}
/**
* Resolves 'self', 'static' and 'parent' to the actual class name.
* @param \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $of
*/
public static function resolve(string $type, $of) : string
{
$lower = \strtolower($type);
if ($of instanceof \ReflectionFunction) {
return $type;
} elseif ($lower === 'self') {
return $of->getDeclaringClass()->name;
} elseif ($lower === 'static') {
return ($of instanceof ReflectionMethod ? $of->getOriginalClass() : $of->getDeclaringClass())->name;
} elseif ($lower === 'parent' && $of->getDeclaringClass()->getParentClass()) {
return $of->getDeclaringClass()->getParentClass()->name;
} else {
return $type;
}
}
private function __construct(array $types, string $kind = '|')
{
$o = \array_search('null', $types, \true);
if ($o !== \false) {
// null as last
\array_splice($types, $o, 1);
$types[] = 'null';
}
$this->types = $types;
$this->simple = \is_string($types[0]) && ($types[1] ?? 'null') === 'null';
$this->kind = \count($types) > 1 ? $kind : '';
}
public function __toString() : string
{
$multi = \count($this->types) > 1;
if ($this->simple) {
return ($multi ? '?' : '') . $this->types[0];
}
$res = [];
foreach ($this->types as $type) {
$res[] = $type instanceof self && $multi ? "({$type})" : $type;
}
return \implode($this->kind, $res);
}
/**
* Returns the array of subtypes that make up the compound type as strings.
* @return array