Skip to content

Commit 6f9749a

Browse files
herndlmondrejmirtes
authored andcommitted
Extract common is_subclass_of and is_a logic
1 parent 70ac622 commit 6f9749a

4 files changed

+79
-82
lines changed

conf/config.neon

+3
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,9 @@ services:
14381438
tags:
14391439
- phpstan.typeSpecifier.functionTypeSpecifyingExtension
14401440

1441+
-
1442+
class: PHPStan\Type\Php\IsAFunctionTypeSpecifyingHelper
1443+
14411444
-
14421445
class: PHPStan\Type\Php\ArrayIsListFunctionTypeSpecifyingExtension
14431446
tags:

src/Type/Php/IsAFunctionTypeSpecifyingExtension.php

+7-41
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,9 @@
99
use PHPStan\Analyser\TypeSpecifierAwareExtension;
1010
use PHPStan\Analyser\TypeSpecifierContext;
1111
use PHPStan\Reflection\FunctionReflection;
12-
use PHPStan\Type\ClassStringType;
1312
use PHPStan\Type\Constant\ConstantBooleanType;
1413
use PHPStan\Type\Constant\ConstantStringType;
1514
use PHPStan\Type\FunctionTypeSpecifyingExtension;
16-
use PHPStan\Type\Generic\GenericClassStringType;
17-
use PHPStan\Type\IntersectionType;
18-
use PHPStan\Type\ObjectType;
19-
use PHPStan\Type\ObjectWithoutClassType;
20-
use PHPStan\Type\Type;
21-
use PHPStan\Type\TypeCombinator;
22-
use PHPStan\Type\TypeTraverser;
23-
use PHPStan\Type\UnionType;
2415
use function count;
2516
use function strtolower;
2617

@@ -29,6 +20,12 @@ class IsAFunctionTypeSpecifyingExtension implements FunctionTypeSpecifyingExtens
2920

3021
private TypeSpecifier $typeSpecifier;
3122

23+
public function __construct(
24+
private IsAFunctionTypeSpecifyingHelper $isAFunctionTypeSpecifyingHelper,
25+
)
26+
{
27+
}
28+
3229
public function isFunctionSupported(FunctionReflection $functionReflection, FuncCall $node, TypeSpecifierContext $context): bool
3330
{
3431
return strtolower($functionReflection->getName()) === 'is_a'
@@ -48,40 +45,9 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
4845
return new SpecifiedTypes([], []);
4946
}
5047

51-
$type = TypeTraverser::map($classType, static function (Type $type, callable $traverse) use ($allowString): Type {
52-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
53-
return $traverse($type);
54-
}
55-
if ($type instanceof ConstantStringType) {
56-
if ($allowString) {
57-
return TypeCombinator::union(
58-
new ObjectType($type->getValue()),
59-
new GenericClassStringType(new ObjectType($type->getValue())),
60-
);
61-
}
62-
return new ObjectType($type->getValue());
63-
}
64-
if ($type instanceof GenericClassStringType) {
65-
if ($allowString) {
66-
return TypeCombinator::union(
67-
$type->getGenericType(),
68-
$type,
69-
);
70-
}
71-
return $type->getGenericType();
72-
}
73-
if ($allowString) {
74-
return TypeCombinator::union(
75-
new ObjectWithoutClassType(),
76-
new ClassStringType(),
77-
);
78-
}
79-
return new ObjectWithoutClassType();
80-
});
81-
8248
return $this->typeSpecifier->create(
8349
$node->getArgs()[0]->value,
84-
$type,
50+
$this->isAFunctionTypeSpecifyingHelper->determineType($classType, $allowString),
8551
$context,
8652
false,
8753
$scope,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PHPStan\Type\ClassStringType;
6+
use PHPStan\Type\Constant\ConstantStringType;
7+
use PHPStan\Type\Generic\GenericClassStringType;
8+
use PHPStan\Type\IntersectionType;
9+
use PHPStan\Type\ObjectType;
10+
use PHPStan\Type\ObjectWithoutClassType;
11+
use PHPStan\Type\Type;
12+
use PHPStan\Type\TypeCombinator;
13+
use PHPStan\Type\TypeTraverser;
14+
use PHPStan\Type\UnionType;
15+
16+
final class IsAFunctionTypeSpecifyingHelper
17+
{
18+
19+
public function determineType(
20+
Type $classType,
21+
bool $allowString,
22+
): Type
23+
{
24+
return TypeTraverser::map(
25+
$classType,
26+
static function (Type $type, callable $traverse) use ($allowString): Type {
27+
if ($type instanceof UnionType || $type instanceof IntersectionType) {
28+
return $traverse($type);
29+
}
30+
if ($type instanceof ConstantStringType) {
31+
if ($allowString) {
32+
return TypeCombinator::union(
33+
new ObjectType($type->getValue()),
34+
new GenericClassStringType(new ObjectType($type->getValue())),
35+
);
36+
}
37+
38+
return new ObjectType($type->getValue());
39+
}
40+
if ($type instanceof GenericClassStringType) {
41+
if ($allowString) {
42+
return TypeCombinator::union(
43+
$type->getGenericType(),
44+
$type,
45+
);
46+
}
47+
48+
return $type->getGenericType();
49+
}
50+
if ($allowString) {
51+
return TypeCombinator::union(
52+
new ObjectWithoutClassType(),
53+
new ClassStringType(),
54+
);
55+
}
56+
57+
return new ObjectWithoutClassType();
58+
},
59+
);
60+
}
61+
62+
}

src/Type/Php/IsSubclassOfFunctionTypeSpecifyingExtension.php

+7-41
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,9 @@
99
use PHPStan\Analyser\TypeSpecifierAwareExtension;
1010
use PHPStan\Analyser\TypeSpecifierContext;
1111
use PHPStan\Reflection\FunctionReflection;
12-
use PHPStan\Type\ClassStringType;
1312
use PHPStan\Type\Constant\ConstantBooleanType;
1413
use PHPStan\Type\Constant\ConstantStringType;
1514
use PHPStan\Type\FunctionTypeSpecifyingExtension;
16-
use PHPStan\Type\Generic\GenericClassStringType;
17-
use PHPStan\Type\IntersectionType;
18-
use PHPStan\Type\ObjectType;
19-
use PHPStan\Type\ObjectWithoutClassType;
20-
use PHPStan\Type\Type;
21-
use PHPStan\Type\TypeCombinator;
22-
use PHPStan\Type\TypeTraverser;
23-
use PHPStan\Type\UnionType;
2415
use function count;
2516
use function strtolower;
2617

@@ -29,6 +20,12 @@ class IsSubclassOfFunctionTypeSpecifyingExtension implements FunctionTypeSpecify
2920

3021
private TypeSpecifier $typeSpecifier;
3122

23+
public function __construct(
24+
private IsAFunctionTypeSpecifyingHelper $isAFunctionTypeSpecifyingHelper,
25+
)
26+
{
27+
}
28+
3229
public function isFunctionSupported(FunctionReflection $functionReflection, FuncCall $node, TypeSpecifierContext $context): bool
3330
{
3431
return strtolower($functionReflection->getName()) === 'is_subclass_of'
@@ -48,40 +45,9 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
4845
return new SpecifiedTypes([], []);
4946
}
5047

51-
$type = TypeTraverser::map($classType, static function (Type $type, callable $traverse) use ($allowString): Type {
52-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
53-
return $traverse($type);
54-
}
55-
if ($type instanceof ConstantStringType) {
56-
if ($allowString) {
57-
return TypeCombinator::union(
58-
new ObjectType($type->getValue()),
59-
new GenericClassStringType(new ObjectType($type->getValue())),
60-
);
61-
}
62-
return new ObjectType($type->getValue());
63-
}
64-
if ($type instanceof GenericClassStringType) {
65-
if ($allowString) {
66-
return TypeCombinator::union(
67-
$type->getGenericType(),
68-
$type,
69-
);
70-
}
71-
return $type->getGenericType();
72-
}
73-
if ($allowString) {
74-
return TypeCombinator::union(
75-
new ObjectWithoutClassType(),
76-
new ClassStringType(),
77-
);
78-
}
79-
return new ObjectWithoutClassType();
80-
});
81-
8248
return $this->typeSpecifier->create(
8349
$node->getArgs()[0]->value,
84-
$type,
50+
$this->isAFunctionTypeSpecifyingHelper->determineType($classType, $allowString),
8551
$context,
8652
false,
8753
$scope,

0 commit comments

Comments
 (0)