Skip to content

Commit ed4ea0a

Browse files
committed
ClassReflection - cannot be a subclass of final-overriden class
1 parent 5b68bd7 commit ed4ea0a

29 files changed

+122
-80
lines changed

Diff for: build/PHPStan/Build/FinalClassRule.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function processNode(Node $node, Scope $scope): array
4444
if ($classReflection->isFinal()) {
4545
return [];
4646
}
47-
if ($classReflection->isSubclassOf(Type::class)) {
47+
if ($classReflection->is(Type::class)) {
4848
return [];
4949
}
5050

Diff for: phpstan-baseline.neon

+9-18
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,15 @@ parameters:
300300
count: 1
301301
path: src/Reflection/BetterReflection/SourceLocator/SkipClassAliasSourceLocator.php
302302

303+
-
304+
message: '''
305+
#^Call to deprecated method isSubclassOf\(\) of class PHPStan\\Reflection\\ClassReflection\:
306+
Use isSubclassOfClass instead\.$#
307+
'''
308+
identifier: method.deprecated
309+
count: 1
310+
path: src/Reflection/ClassReflection.php
311+
303312
-
304313
message: '#^Doing instanceof PHPStan\\Type\\Generic\\GenericObjectType is error\-prone and deprecated\.$#'
305314
identifier: phpstanApi.instanceofType
@@ -1317,12 +1326,6 @@ parameters:
13171326
count: 2
13181327
path: src/Type/IntegerType.php
13191328

1320-
-
1321-
message: '#^Doing instanceof PHPStan\\Type\\Accessory\\AccessoryType is error\-prone and deprecated\. Use methods on PHPStan\\Type\\Type instead\.$#'
1322-
identifier: phpstanApi.instanceofType
1323-
count: 3
1324-
path: src/Type/IntersectionType.php
1325-
13261329
-
13271330
message: '#^Doing instanceof PHPStan\\Type\\ArrayType is error\-prone and deprecated\. Use Type\:\:isArray\(\) or Type\:\:getArrays\(\) instead\.$#'
13281331
identifier: phpstanApi.instanceofType
@@ -1647,12 +1650,6 @@ parameters:
16471650
count: 2
16481651
path: src/Type/StringType.php
16491652

1650-
-
1651-
message: '#^Doing instanceof PHPStan\\Type\\Accessory\\AccessoryType is error\-prone and deprecated\. Use methods on PHPStan\\Type\\Type instead\.$#'
1652-
identifier: phpstanApi.instanceofType
1653-
count: 1
1654-
path: src/Type/TypeCombinator.php
1655-
16561653
-
16571654
message: '#^Doing instanceof PHPStan\\Type\\ArrayType is error\-prone and deprecated\. Use Type\:\:isArray\(\) or Type\:\:getArrays\(\) instead\.$#'
16581655
identifier: phpstanApi.instanceofType
@@ -1815,12 +1812,6 @@ parameters:
18151812
count: 1
18161813
path: src/Type/UnionType.php
18171814

1818-
-
1819-
message: '#^Doing instanceof PHPStan\\Type\\Accessory\\AccessoryType is error\-prone and deprecated\. Use methods on PHPStan\\Type\\Type instead\.$#'
1820-
identifier: phpstanApi.instanceofType
1821-
count: 3
1822-
path: src/Type/UnionTypeHelper.php
1823-
18241815
-
18251816
message: '#^Doing instanceof PHPStan\\Type\\CallableType is error\-prone and deprecated\. Use Type\:\:isCallable\(\) and Type\:\:getCallableParametersAcceptors\(\) instead\.$#'
18261817
identifier: phpstanApi.instanceofType

Diff for: src/Analyser/MutatingScope.php

+12-12
Original file line numberDiff line numberDiff line change
@@ -5447,22 +5447,22 @@ public function canWriteProperty(ExtendedPropertyReflection $propertyReflection)
54475447
return $this->canAccessClassMember($propertyReflection);
54485448
}
54495449

5450-
$classReflectionName = $propertyReflection->getDeclaringClass()->getName();
5451-
$canAccessClassMember = static function (ClassReflection $classReflection) use ($propertyReflection, $classReflectionName) {
5450+
$propertyDeclaringClass = $propertyReflection->getDeclaringClass();
5451+
$canAccessClassMember = static function (ClassReflection $classReflection) use ($propertyReflection, $propertyDeclaringClass) {
54525452
if ($propertyReflection->isPrivateSet()) {
5453-
return $classReflection->getName() === $classReflectionName;
5453+
return $classReflection->getName() === $propertyDeclaringClass->getName();
54545454
}
54555455

54565456
// protected set
54575457

54585458
if (
5459-
$classReflection->getName() === $classReflectionName
5460-
|| $classReflection->isSubclassOf($classReflectionName)
5459+
$classReflection->getName() === $propertyDeclaringClass->getName()
5460+
|| $classReflection->isSubclassOfClass($propertyDeclaringClass)
54615461
) {
54625462
return true;
54635463
}
54645464

5465-
return $propertyReflection->getDeclaringClass()->isSubclassOf($classReflection->getName());
5465+
return $propertyReflection->getDeclaringClass()->isSubclassOfClass($classReflection);
54665466
};
54675467

54685468
foreach ($this->inClosureBindScopeClasses as $inClosureBindScopeClass) {
@@ -5504,22 +5504,22 @@ private function canAccessClassMember(ClassMemberReflection $classMemberReflecti
55045504
return true;
55055505
}
55065506

5507-
$classReflectionName = $classMemberReflection->getDeclaringClass()->getName();
5508-
$canAccessClassMember = static function (ClassReflection $classReflection) use ($classMemberReflection, $classReflectionName) {
5507+
$classMemberDeclaringClass = $classMemberReflection->getDeclaringClass();
5508+
$canAccessClassMember = static function (ClassReflection $classReflection) use ($classMemberReflection, $classMemberDeclaringClass) {
55095509
if ($classMemberReflection->isPrivate()) {
5510-
return $classReflection->getName() === $classReflectionName;
5510+
return $classReflection->getName() === $classMemberDeclaringClass->getName();
55115511
}
55125512

55135513
// protected
55145514

55155515
if (
5516-
$classReflection->getName() === $classReflectionName
5517-
|| $classReflection->isSubclassOf($classReflectionName)
5516+
$classReflection->getName() === $classMemberDeclaringClass->getName()
5517+
|| $classReflection->isSubclassOfClass($classMemberDeclaringClass)
55185518
) {
55195519
return true;
55205520
}
55215521

5522-
return $classMemberReflection->getDeclaringClass()->isSubclassOf($classReflection->getName());
5522+
return $classMemberReflection->getDeclaringClass()->isSubclassOfClass($classReflection);
55235523
};
55245524

55255525
foreach ($this->inClosureBindScopeClasses as $inClosureBindScopeClass) {

Diff for: src/Analyser/NodeScopeResolver.php

+3-6
Original file line numberDiff line numberDiff line change
@@ -2970,10 +2970,7 @@ static function (): void {
29702970
&& $scopeFunction instanceof MethodReflection
29712971
&& !$scopeFunction->isStatic()
29722972
&& $scope->isInClass()
2973-
&& (
2974-
$scope->getClassReflection()->getName() === $methodReflection->getDeclaringClass()->getName()
2975-
|| $scope->getClassReflection()->isSubclassOf($methodReflection->getDeclaringClass()->getName())
2976-
)
2973+
&& $scope->getClassReflection()->is($methodReflection->getDeclaringClass()->getName())
29772974
) {
29782975
$scope = $scope->invalidateExpression(new Variable('this'), true);
29792976
}
@@ -2985,7 +2982,7 @@ static function (): void {
29852982
&& $scopeFunction instanceof MethodReflection
29862983
&& !$scopeFunction->isStatic()
29872984
&& $scope->isInClass()
2988-
&& $scope->getClassReflection()->isSubclassOf($methodReflection->getDeclaringClass()->getName())
2985+
&& $scope->getClassReflection()->isSubclassOfClass($methodReflection->getDeclaringClass())
29892986
) {
29902987
$thisType = $scope->getType(new Variable('this'));
29912988
$methodClassReflection = $methodReflection->getDeclaringClass();
@@ -4215,7 +4212,7 @@ private function getConstructorThrowPoint(MethodReflection $constructorReflectio
42154212
return ThrowPoint::createExplicit($scope, $throwType, $new, true);
42164213
}
42174214
} elseif ($this->implicitThrows) {
4218-
if ($classReflection->getName() !== Throwable::class && !$classReflection->isSubclassOf(Throwable::class)) {
4215+
if (!$classReflection->is(Throwable::class)) {
42194216
return ThrowPoint::createImplicit($scope, $methodCall);
42204217
}
42214218
}

Diff for: src/Analyser/Scope.php

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ public function getIterableKeyType(Type $iteratee): Type;
8585

8686
public function getIterableValueType(Type $iteratee): Type;
8787

88+
/**
89+
* @phpstan-assert-if-true !null $this->getAnonymousFunctionReflection()
90+
* @phpstan-assert-if-true !null $this->getAnonymousFunctionReturnType()
91+
*/
8892
public function isInAnonymousFunction(): bool;
8993

9094
public function getAnonymousFunctionReflection(): ?ParametersAcceptor;

Diff for: src/Reflection/ClassReflection.php

+19-6
Original file line numberDiff line numberDiff line change
@@ -837,20 +837,33 @@ public function is(string $className): bool
837837
return $this->getName() === $className || $this->isSubclassOf($className);
838838
}
839839

840+
/**
841+
* @deprecated Use isSubclassOfClass instead.
842+
*/
840843
public function isSubclassOf(string $className): bool
841844
{
842-
if (isset($this->subclasses[$className])) {
843-
return $this->subclasses[$className];
845+
if (!$this->reflectionProvider->hasClass($className)) {
846+
return false;
844847
}
845848

846-
if (!$this->reflectionProvider->hasClass($className)) {
847-
return $this->subclasses[$className] = false;
849+
return $this->isSubclassOfClass($this->reflectionProvider->getClass($className));
850+
}
851+
852+
public function isSubclassOfClass(self $class): bool
853+
{
854+
$cacheKey = $class->getCacheKey();
855+
if (isset($this->subclasses[$cacheKey])) {
856+
return $this->subclasses[$cacheKey];
857+
}
858+
859+
if ($class->isFinal() || $class->isAnonymous()) {
860+
return $this->subclasses[$cacheKey] = false;
848861
}
849862

850863
try {
851-
return $this->subclasses[$className] = $this->reflection->isSubclassOf($className);
864+
return $this->subclasses[$cacheKey] = $this->reflection->isSubclassOf($class->getName());
852865
} catch (ReflectionException) {
853-
return $this->subclasses[$className] = false;
866+
return $this->subclasses[$cacheKey] = false;
854867
}
855868
}
856869

Diff for: src/Reflection/Php/Soap/SoapClientMethodsClassReflectionExtension.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ final class SoapClientMethodsClassReflectionExtension implements MethodsClassRef
1111

1212
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
1313
{
14-
return $classReflection->getName() === 'SoapClient' || $classReflection->isSubclassOf('SoapClient');
14+
return $classReflection->is('SoapClient');
1515
}
1616

1717
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection

Diff for: src/Reflection/Php/UniversalObjectCratesClassReflectionExtension.php

+1-4
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,7 @@ private static function isUniversalObjectCrateImplementation(
5959
continue;
6060
}
6161

62-
if (
63-
$classReflection->getName() === $className
64-
|| $classReflection->isSubclassOf($className)
65-
) {
62+
if ($classReflection->is($className)) {
6663
return true;
6764
}
6865
}

Diff for: src/Rules/Api/ApiInstanceofRule.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public function processNode(Node $node, Scope $scope): array
8181
*/
8282
private function processCoveredClass(Node\Expr\Instanceof_ $node, Scope $scope, ClassReflection $classReflection): array
8383
{
84-
if ($classReflection->getName() === Type::class || $classReflection->isSubclassOf(Type::class)) {
84+
if ($classReflection->is(Type::class)) {
8585
return [];
8686
}
8787
if ($classReflection->isInterface()) {

Diff for: src/Rules/Api/ApiInstanceofTypeRule.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public function processNode(Node $node, Scope $scope): array
128128

129129
if ($this->reflectionProvider->hasClass($className)) {
130130
$classReflection = $this->reflectionProvider->getClass($className);
131-
if ($classReflection->isSubclassOf(AccessoryType::class)) {
131+
if ($classReflection->is(AccessoryType::class)) {
132132
if ($className === $classReflection->getName()) {
133133
return [];
134134
}

Diff for: src/Rules/Exceptions/DefaultExceptionTypeResolver.php

+2-10
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,7 @@ public function isCheckedException(string $className, Scope $scope): bool
4949

5050
$classReflection = $this->reflectionProvider->getClass($className);
5151
foreach ($this->uncheckedExceptionClasses as $uncheckedExceptionClass) {
52-
if ($classReflection->getName() === $uncheckedExceptionClass) {
53-
return false;
54-
}
55-
56-
if (!$classReflection->isSubclassOf($uncheckedExceptionClass)) {
52+
if (!$classReflection->is($uncheckedExceptionClass)) {
5753
continue;
5854
}
5955

@@ -83,11 +79,7 @@ private function isCheckedExceptionInternal(string $className): bool
8379

8480
$classReflection = $this->reflectionProvider->getClass($className);
8581
foreach ($this->checkedExceptionClasses as $checkedExceptionClass) {
86-
if ($classReflection->getName() === $checkedExceptionClass) {
87-
return true;
88-
}
89-
90-
if (!$classReflection->isSubclassOf($checkedExceptionClass)) {
82+
if (!$classReflection->is($checkedExceptionClass)) {
9183
continue;
9284
}
9385

Diff for: src/Rules/Functions/ArrowFunctionReturnTypeRule.php

-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use PHPStan\ShouldNotHappenException;
1212
use PHPStan\Type\NeverType;
1313
use PHPStan\Type\ObjectType;
14-
use PHPStan\Type\Type;
1514

1615
/**
1716
* @implements Rule<InArrowFunctionNode>
@@ -34,7 +33,6 @@ public function processNode(Node $node, Scope $scope): array
3433
throw new ShouldNotHappenException();
3534
}
3635

37-
/** @var Type $returnType */
3836
$returnType = $scope->getAnonymousFunctionReturnType();
3937
$generatorType = new ObjectType(Generator::class);
4038

Diff for: src/Rules/Functions/ClosureReturnTypeRule.php

-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use PHPStan\Node\ClosureReturnStatementsNode;
88
use PHPStan\Rules\FunctionReturnTypeCheck;
99
use PHPStan\Rules\Rule;
10-
use PHPStan\Type\Type;
1110
use PHPStan\Type\TypeCombinator;
1211

1312
/**
@@ -31,7 +30,6 @@ public function processNode(Node $node, Scope $scope): array
3130
return [];
3231
}
3332

34-
/** @var Type $returnType */
3533
$returnType = $scope->getAnonymousFunctionReturnType();
3634
$containsNull = TypeCombinator::containsNull($returnType);
3735
$hasNativeTypehint = $node->getClosureExpr()->returnType !== null;

Diff for: src/Rules/Methods/ReturnTypeRule.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public function processNode(Node $node, Scope $scope): array
9494
&& $errors[0]->getIdentifier() === 'return.type'
9595
&& !$errors[0] instanceof TipRuleError
9696
&& $errors[0] instanceof LineRuleError
97-
&& $method->getDeclaringClass()->isSubclassOf(Rule::class)
97+
&& $method->getDeclaringClass()->is(Rule::class)
9898
&& strtolower($method->getName()) === 'processnode'
9999
&& $node->expr !== null
100100
) {

Diff for: src/Rules/Methods/StaticMethodCallCheck.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public function check(
221221
$classType->getObjectClassNames(),
222222
static fn (string $objectClassName) => TrinaryLogic::createFromBoolean(
223223
$scope->isInClass()
224-
&& ($scope->getClassReflection()->getName() === $objectClassName || $scope->getClassReflection()->isSubclassOf($objectClassName)),
224+
&& $scope->getClassReflection()->is($objectClassName),
225225
),
226226
);
227227
if (

Diff for: src/Rules/PhpDoc/VarTagTypeRuleHelper.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\PhpDoc\NameScopeAlreadyBeingCreatedException;
1111
use PHPStan\PhpDoc\Tag\VarTag;
1212
use PHPStan\PhpDoc\TypeNodeResolver;
13+
use PHPStan\Reflection\ReflectionProvider;
1314
use PHPStan\Rules\IdentifierRuleError;
1415
use PHPStan\Rules\RuleErrorBuilder;
1516
use PHPStan\Type\ArrayType;
@@ -31,6 +32,7 @@ final class VarTagTypeRuleHelper
3132
public function __construct(
3233
private TypeNodeResolver $typeNodeResolver,
3334
private FileTypeMapper $fileTypeMapper,
35+
private ReflectionProvider $reflectionProvider,
3436
private bool $checkTypeAgainstPhpDocType,
3537
private bool $strictWideningCheck,
3638
)
@@ -125,8 +127,13 @@ public function checkExprType(Scope $scope, Node\Expr $expr, Type $varTagType):
125127
private function containsPhpStanType(Type $type): bool
126128
{
127129
$classReflections = TypeUtils::toBenevolentUnion($type)->getObjectClassReflections();
130+
if (!$this->reflectionProvider->hasClass(Type::class)) {
131+
return false;
132+
}
133+
134+
$typeClass = $this->reflectionProvider->getClass(Type::class);
128135
foreach ($classReflections as $classReflection) {
129-
if (!$classReflection->isSubclassOf(Type::class)) {
136+
if (!$classReflection->isSubclassOfClass($typeClass)) {
130137
continue;
131138
}
132139

Diff for: src/Type/ObjectType.php

+5-8
Original file line numberDiff line numberDiff line change
@@ -414,11 +414,11 @@ public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
414414
return self::$superTypes[$thisDescription][$description] = $transformResult(IsSuperTypeOfResult::createYes());
415415
}
416416

417-
if ($thatClassReflection->isSubclassOf($thisClassName)) {
417+
if ($thatClassReflection->isSubclassOfClass($thisClassReflection)) {
418418
return self::$superTypes[$thisDescription][$description] = $transformResult(IsSuperTypeOfResult::createYes());
419419
}
420420

421-
if ($thisClassReflection->isSubclassOf($thatClassNames[0])) {
421+
if ($thisClassReflection->isSubclassOfClass($thatClassReflection)) {
422422
return self::$superTypes[$thisDescription][$description] = IsSuperTypeOfResult::createMaybe();
423423
}
424424

@@ -485,7 +485,7 @@ private function checkSubclassAcceptability(string $thatClass): AcceptsResult
485485
}
486486

487487
return AcceptsResult::createFromBoolean(
488-
$thatReflection->isSubclassOf($thisReflection->getName()),
488+
$thatReflection->isSubclassOfClass($thisReflection),
489489
);
490490
}
491491

@@ -1127,10 +1127,7 @@ private function isExtraOffsetAccessibleClass(): TrinaryLogic
11271127
}
11281128

11291129
foreach (self::EXTRA_OFFSET_CLASSES as $extraOffsetClass) {
1130-
if ($classReflection->getName() === $extraOffsetClass) {
1131-
return TrinaryLogic::createYes();
1132-
}
1133-
if ($classReflection->isSubclassOf($extraOffsetClass)) {
1130+
if ($classReflection->is($extraOffsetClass)) {
11341131
return TrinaryLogic::createYes();
11351132
}
11361133
}
@@ -1370,7 +1367,7 @@ public function isInstanceOf(string $className): TrinaryLogic
13701367
return TrinaryLogic::createMaybe();
13711368
}
13721369

1373-
if ($classReflection->getName() === $className || $classReflection->isSubclassOf($className)) {
1370+
if ($classReflection->is($className)) {
13741371
return TrinaryLogic::createYes();
13751372
}
13761373

0 commit comments

Comments
 (0)