<?php
namespace Symfony\Component\TypeInfo\TypeResolver;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use Psr\Container\ContainerInterface;
use Symfony\Component\TypeInfo\Exception\UnsupportedException;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\TypeContext\TypeContext;
use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory;
final class TypeResolver implements TypeResolverInterface
{
public function __construct(
private readonly ContainerInterface $resolvers,
) {
}
public function resolve(mixed $subject, ?TypeContext $typeContext = null): Type
{
$subjectType = match (\is_object($subject)) {
true => match (true) {
is_subclass_of($subject::class, \ReflectionType::class) => \ReflectionType::class,
is_subclass_of($subject::class, \ReflectionFunctionAbstract::class) => \ReflectionFunctionAbstract::class,
default => $subject::class,
},
false => get_debug_type($subject),
};
if (!$this->resolvers->has($subjectType)) {
if ('string' === $subjectType) {
throw new UnsupportedException('Cannot find any resolver for "string" type. Try running "composer require phpstan/phpdoc-parser".', $subject);
}
throw new UnsupportedException(\sprintf('Cannot find any resolver for "%s" type.', $subjectType), $subject);
}
$resolver = $this->resolvers->get($subjectType);
return $resolver->resolve($subject, $typeContext);
}
public static function create(?array $resolvers = null): self
{
if (null === $resolvers) {
$stringTypeResolver = class_exists(PhpDocParser::class) ? new StringTypeResolver() : null;
$typeContextFactory = new TypeContextFactory($stringTypeResolver);
$reflectionTypeResolver = new ReflectionTypeResolver();
$resolvers = [
\ReflectionType::class => $reflectionTypeResolver,
\ReflectionParameter::class => new ReflectionParameterTypeResolver($reflectionTypeResolver, $typeContextFactory),
\ReflectionProperty::class => new ReflectionPropertyTypeResolver($reflectionTypeResolver, $typeContextFactory),
\ReflectionFunctionAbstract::class => new ReflectionReturnTypeResolver($reflectionTypeResolver, $typeContextFactory),
];
if (null !== $stringTypeResolver) {
$resolvers['string'] = $stringTypeResolver;
$resolvers[\ReflectionParameter::class] = new PhpDocAwareReflectionTypeResolver($resolvers[\ReflectionParameter::class], $stringTypeResolver, $typeContextFactory);
$resolvers[\ReflectionProperty::class] = new PhpDocAwareReflectionTypeResolver($resolvers[\ReflectionProperty::class], $stringTypeResolver, $typeContextFactory);
$resolvers[\ReflectionFunctionAbstract::class] = new PhpDocAwareReflectionTypeResolver($resolvers[\ReflectionFunctionAbstract::class], $stringTypeResolver, $typeContextFactory);
}
}
$resolversContainer = new class($resolvers) implements ContainerInterface {
public function __construct(
private readonly array $resolvers,
) {
}
public function has(string $id): bool
{
return isset($this->resolvers[$id]);
}
public function get(string $id): TypeResolverInterface
{
return $this->resolvers[$id];
}
};
return new self($resolversContainer);
}
}