, NodeTypeResolverInterface>
*/
private $nodeTypeResolvers = [];
/**
* @param NodeTypeResolverInterface[] $nodeTypeResolvers
*/
public function __construct(ObjectTypeSpecifier $objectTypeSpecifier, ClassAnalyzer $classAnalyzer, GenericClassStringTypeCorrector $genericClassStringTypeCorrector, ReflectionProvider $reflectionProvider, AccessoryNonEmptyStringTypeCorrector $accessoryNonEmptyStringTypeCorrector, RenamedClassesDataCollector $renamedClassesDataCollector, iterable $nodeTypeResolvers)
{
$this->objectTypeSpecifier = $objectTypeSpecifier;
$this->classAnalyzer = $classAnalyzer;
$this->genericClassStringTypeCorrector = $genericClassStringTypeCorrector;
$this->reflectionProvider = $reflectionProvider;
$this->accessoryNonEmptyStringTypeCorrector = $accessoryNonEmptyStringTypeCorrector;
$this->renamedClassesDataCollector = $renamedClassesDataCollector;
foreach ($nodeTypeResolvers as $nodeTypeResolver) {
if ($nodeTypeResolver instanceof NodeTypeResolverAwareInterface) {
$nodeTypeResolver->autowire($this);
}
foreach ($nodeTypeResolver->getNodeClasses() as $nodeClass) {
$this->nodeTypeResolvers[$nodeClass] = $nodeTypeResolver;
}
}
}
/**
* @api doctrine symfony
* @param ObjectType[] $requiredTypes
*/
public function isObjectTypes(Node $node, array $requiredTypes) : bool
{
foreach ($requiredTypes as $requiredType) {
if ($this->isObjectType($node, $requiredType)) {
return \true;
}
}
return \false;
}
public function isObjectType(Node $node, ObjectType $requiredObjectType) : bool
{
if ($node instanceof ClassConstFetch) {
return \false;
}
// warn about invalid use of this method
if ($node instanceof ClassMethod || $node instanceof ClassConst) {
throw new ShouldNotHappenException(\sprintf(self::ERROR_MESSAGE, \get_class($node), ClassLike::class));
}
$resolvedType = $this->getType($node);
if ($resolvedType instanceof MixedType) {
return \false;
}
if ($resolvedType instanceof ThisType) {
$resolvedType = $resolvedType->getStaticObjectType();
}
if ($resolvedType instanceof ObjectType) {
try {
return $this->resolveObjectType($resolvedType, $requiredObjectType);
} catch (ClassAutoloadingException $exception) {
// in some type checks, the provided type in rector.php configuration does not have to exists
return \false;
}
}
if ($resolvedType instanceof ObjectWithoutClassType) {
return $this->isMatchObjectWithoutClassType($resolvedType, $requiredObjectType);
}
return $this->isMatchingUnionType($resolvedType, $requiredObjectType);
}
public function getType(Node $node) : Type
{
if ($node instanceof NullableType) {
$type = $this->getType($node->type);
if (!$type instanceof MixedType) {
return new UnionType([$type, new NullType()]);
}
}
if ($node instanceof Ternary) {
$ternaryType = $this->resolveTernaryType($node);
if (!$ternaryType instanceof MixedType) {
return $ternaryType;
}
}
if ($node instanceof Coalesce) {
$first = $this->getType($node->left);
$second = $this->getType($node->right);
if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
}
$type = $this->resolveByNodeTypeResolvers($node);
if ($type instanceof Type) {
$type = $this->accessoryNonEmptyStringTypeCorrector->correct($type);
$type = $this->genericClassStringTypeCorrector->correct($type);
if ($type instanceof ObjectType) {
$scope = $node->getAttribute(AttributeKey::SCOPE);
$type = $this->objectTypeSpecifier->narrowToFullyQualifiedOrAliasedObjectType($node, $type, $scope);
}
return $type;
}
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (!$scope instanceof Scope) {
return new MixedType();
}
if ($node instanceof NodeUnionType) {
$types = [];
foreach ($node->types as $type) {
$types[] = $this->getType($type);
}
return new UnionType($types);
}
if (!$node instanceof Expr) {
return new MixedType();
}
$type = $scope->getType($node);
$type = $this->accessoryNonEmptyStringTypeCorrector->correct($type);
$type = $this->genericClassStringTypeCorrector->correct($type);
// hot fix for phpstan not resolving chain method calls
if (!$node instanceof MethodCall) {
return $type;
}
if (!$type instanceof MixedType) {
return $type;
}
return $this->getType($node->var);
}
/**
* e.g. string|null, ObjectNull|null
*/
public function isNullableType(Node $node) : bool
{
$nodeType = $this->getType($node);
return TypeCombinator::containsNull($nodeType);
}
public function getNativeType(Expr $expr) : Type
{
$scope = $expr->getAttribute(AttributeKey::SCOPE);
if (!$scope instanceof Scope) {
return new MixedType();
}
// cover direct New_ class
if ($this->classAnalyzer->isAnonymousClass($expr)) {
$type = $this->nodeTypeResolvers[New_::class]->resolve($expr);
if ($type instanceof ObjectWithoutClassType) {
return $type;
}
}
$type = $this->resolveNativeTypeWithBuiltinMethodCallFallback($expr, $scope);
if ($expr instanceof ArrayDimFetch) {
$type = $this->resolveArrayDimFetchType($expr, $scope, $type);
}
if (!$type instanceof UnionType) {
if ($this->isAnonymousObjectType($type)) {
return new ObjectWithoutClassType();
}
return $this->accessoryNonEmptyStringTypeCorrector->correct($type);
}
return $this->resolveNativeUnionType($type);
}
public function isNumberType(Expr $expr) : bool
{
$nodeType = $this->getNativeType($expr);
if ($nodeType->isInteger()->yes()) {
return \true;
}
return $nodeType->isFloat()->yes();
}
/**
* @api
* @param class-string