Skip to content

Commit da34d3f

Browse files
committed
Type system: ConstantStringType created from ::class is always considered a classname
1 parent 0319ace commit da34d3f

File tree

6 files changed

+57
-28
lines changed

6 files changed

+57
-28
lines changed

src/Type/ClassStringType.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace PHPStan\Type;
44

5-
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
65
use PHPStan\TrinaryLogic;
76
use PHPStan\Type\Constant\ConstantStringType;
87

@@ -28,8 +27,7 @@ public function accepts(Type $type, bool $strictTypes): TrinaryLogic
2827
}
2928

3029
if ($type instanceof ConstantStringType) {
31-
$reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
32-
return TrinaryLogic::createFromBoolean($reflectionProvider->hasClass($type->getValue()));
30+
return TrinaryLogic::createFromBoolean($type->isClassString());
3331
}
3432

3533
if ($type instanceof self) {
@@ -46,8 +44,7 @@ public function accepts(Type $type, bool $strictTypes): TrinaryLogic
4644
public function isSuperTypeOf(Type $type): TrinaryLogic
4745
{
4846
if ($type instanceof ConstantStringType) {
49-
$reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
50-
return TrinaryLogic::createFromBoolean($reflectionProvider->hasClass($type->getValue()));
47+
return TrinaryLogic::createFromBoolean($type->isClassString());
5148
}
5249

5350
if ($type instanceof self) {

src/Type/Constant/ConstantStringType.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,13 @@ public function getValue(): string
5757

5858
public function isClassString(): bool
5959
{
60-
return $this->isClassString;
60+
if ($this->isClassString) {
61+
return true;
62+
}
63+
64+
$reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
65+
66+
return $reflectionProvider->hasClass($this->value);
6167
}
6268

6369
public function describe(VerbosityLevel $level): string
@@ -118,9 +124,7 @@ public function isSuperTypeOf(Type $type): TrinaryLogic
118124
return TrinaryLogic::createNo();
119125
}
120126
if ($type instanceof ClassStringType) {
121-
$reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
122-
123-
return $reflectionProvider->hasClass($this->getValue()) ? TrinaryLogic::createMaybe() : TrinaryLogic::createNo();
127+
return $this->isClassString() ? TrinaryLogic::createMaybe() : TrinaryLogic::createNo();
124128
}
125129

126130
if ($type instanceof self) {

src/Type/Generic/GenericClassStringType.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace PHPStan\Type\Generic;
44

5-
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
65
use PHPStan\TrinaryLogic;
76
use PHPStan\Type\ClassStringType;
87
use PHPStan\Type\CompoundType;
@@ -53,8 +52,7 @@ public function accepts(Type $type, bool $strictTypes): TrinaryLogic
5352
}
5453

5554
if ($type instanceof ConstantStringType) {
56-
$reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
57-
if (!$reflectionProvider->hasClass($type->getValue())) {
55+
if (!$type->isClassString()) {
5856
return TrinaryLogic::createNo();
5957
}
6058

src/Type/Php/ClassExistsFunctionTypeSpecifyingExtension.php

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
use PHPStan\Type\Constant\ConstantBooleanType;
1717
use PHPStan\Type\Constant\ConstantStringType;
1818
use PHPStan\Type\FunctionTypeSpecifyingExtension;
19-
use PHPStan\Type\NeverType;
20-
use PHPStan\Type\TypeCombinator;
2119

2220
class ClassExistsFunctionTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension
2321
{
@@ -41,20 +39,16 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
4139
{
4240
$argType = $scope->getType($node->getArgs()[0]->value);
4341
$classStringType = new ClassStringType();
44-
if (TypeCombinator::intersect($argType, $classStringType) instanceof NeverType) {
45-
if ($argType instanceof ConstantStringType) {
46-
return $this->typeSpecifier->create(
47-
new FuncCall(new FullyQualified('class_exists'), [
48-
new Arg(new String_(ltrim($argType->getValue(), '\\'))),
49-
]),
50-
new ConstantBooleanType(true),
51-
$context,
52-
false,
53-
$scope
54-
);
55-
}
56-
57-
return new SpecifiedTypes();
42+
if ($argType instanceof ConstantStringType) {
43+
return $this->typeSpecifier->create(
44+
new FuncCall(new FullyQualified('class_exists'), [
45+
new Arg(new String_(ltrim($argType->getValue(), '\\'))),
46+
]),
47+
new ConstantBooleanType(true),
48+
$context,
49+
false,
50+
$scope
51+
);
5852
}
5953

6054
return $this->typeSpecifier->create(

tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,4 +564,9 @@ public function testBug5218(bool $checkExplicitMixed, array $errors): void
564564
$this->analyse([__DIR__ . '/data/bug-5218.php'], $errors);
565565
}
566566

567+
public function testBug5979(): void
568+
{
569+
$this->analyse([__DIR__ . '/data/bug-5979.php'], []);
570+
}
571+
567572
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug5979;
4+
5+
class HelloWorld
6+
{
7+
/**
8+
* @return list<array{string, class-string}>
9+
*/
10+
public function dataProviderForTestValidCommands(): array
11+
{
12+
$data = [
13+
// left out some commands here for simplicity ...
14+
// [...]
15+
[
16+
'migrations:execute',
17+
SplQueue::class,
18+
],
19+
];
20+
21+
// this is only available with DBAL 2.x
22+
if (class_exists(ImportCommand::class)) {
23+
$data[] = [
24+
'dbal:import',
25+
ImportCommand::class,
26+
];
27+
}
28+
29+
return $data;
30+
}
31+
}

0 commit comments

Comments
 (0)