Skip to content

Commit 1aecdf2

Browse files
authored
Upgrade to phpstan 2.0 (#12)
1 parent 1788f68 commit 1aecdf2

12 files changed

+86
-49
lines changed

.github/workflows/static.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ on:
2222
name: static analysis
2323

2424
jobs:
25-
psalm:
25+
phpstan:
2626
uses: php-forge/actions/.github/workflows/phpstan.yml@main
2727
with:
2828
composer-command: |

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
"require": {
1111
"php": ">=8.1",
1212
"nikic/php-parser": "^4.1",
13-
"phpstan/phpstan": "^1.10",
14-
"yiisoft/yii2": "^2.0.49 || ^2.2"
13+
"phpstan/phpstan": "^2.0",
14+
"yiisoft/yii2": "^2.0.49 || ^22"
1515
},
1616
"require-dev": {
1717
"maglnet/composer-require-checker": "^4.7",
18-
"phpstan/phpstan-phpunit": "^1.0",
18+
"phpstan/phpstan-phpunit": "^2.0",
1919
"phpunit/phpunit": "^10.2",
2020
"symplify/easy-coding-standard": "^12.1"
2121
},

phpstan.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ includes:
22
- extension.neon
33
- vendor/phpstan/phpstan-phpunit/extension.neon
44
- vendor/phpstan/phpstan-phpunit/rules.neon
5+
- phar://phpstan.phar/conf/bleedingEdge.neon
56

67
parameters:
78
ignoreErrors:

src/Reflection/ApplicationPropertiesClassReflectionExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ final class ApplicationPropertiesClassReflectionExtension implements PropertiesC
2121
public function __construct(
2222
private readonly AnnotationsPropertiesClassReflectionExtension $annotationsProperties,
2323
private readonly ReflectionProvider $reflectionProvider,
24-
private readonly ServiceMap $serviceMap
24+
private readonly ServiceMap $serviceMap,
2525
) {}
2626

2727
public function hasProperty(ClassReflection $classReflection, string $propertyName): bool
@@ -49,7 +49,7 @@ public function getProperty(ClassReflection $classReflection, string $propertyNa
4949
}
5050

5151
if (null !== $componentClass = $this->serviceMap->getComponentClassById($propertyName)) {
52-
return new ComponentPropertyReflection(new DummyPropertyReflection(), new ObjectType($componentClass));
52+
return new ComponentPropertyReflection(new DummyPropertyReflection($propertyName), new ObjectType($componentClass));
5353
}
5454

5555
if ($classReflection->hasNativeProperty($propertyName)) {

src/Reflection/UserPropertiesClassReflectionExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function hasProperty(ClassReflection $classReflection, string $propertyNa
3333
public function getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection
3434
{
3535
if ($propertyName === 'identity') {
36-
return new ComponentPropertyReflection(new DummyPropertyReflection(), new MixedType());
36+
return new ComponentPropertyReflection(new DummyPropertyReflection($propertyName), new MixedType());
3737
}
3838

3939
if ($classReflection->hasNativeProperty($propertyName)) {

src/Type/ActiveQueryDynamicMethodReturnTypeExtension.php

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use PhpParser\Node\Expr\MethodCall;
99
use PHPStan\Analyser\Scope;
1010
use PHPStan\Reflection\MethodReflection;
11-
use PHPStan\Reflection\ParametersAcceptorSelector;
1211
use PHPStan\ShouldNotHappenException;
1312
use PHPStan\Type\ArrayType;
1413
use PHPStan\Type\Constant\ConstantBooleanType;
@@ -38,11 +37,12 @@ public function getClass(): string
3837
*/
3938
public function isMethodSupported(MethodReflection $methodReflection): bool
4039
{
41-
if (
42-
ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())
43-
->getReturnType() instanceof ThisType
44-
) {
45-
return true;
40+
$variants = $methodReflection->getVariants();
41+
if (count($variants) > 0) {
42+
$returnType = $variants[0]->getReturnType();
43+
if ($returnType instanceof ThisType) {
44+
return true;
45+
}
4646
}
4747

4848
return in_array($methodReflection->getName(), ['one', 'all'], true);
@@ -54,7 +54,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
5454
public function getTypeFromMethodCall(
5555
MethodReflection $methodReflection,
5656
MethodCall $methodCall,
57-
Scope $scope
57+
Scope $scope,
5858
): Type {
5959
$calledOnType = $scope->getType($methodCall->var);
6060
if (!$calledOnType instanceof ActiveQueryObjectType) {
@@ -63,7 +63,7 @@ public function getTypeFromMethodCall(
6363
'Unexpected type %s during method call %s at line %d',
6464
get_class($calledOnType),
6565
$methodReflection->getName(),
66-
$methodCall->getLine()
66+
$methodCall->getLine(),
6767
),
6868
);
6969
}
@@ -72,13 +72,19 @@ public function getTypeFromMethodCall(
7272
if ($methodName === 'asArray') {
7373
$argType = isset($methodCall->args[0]) && $methodCall->args[0] instanceof Arg
7474
? $scope->getType($methodCall->args[0]->value) : new ConstantBooleanType(true);
75-
if (!$argType instanceof ConstantBooleanType) {
75+
76+
$boolValue = true;
77+
if ($argType->isTrue()->yes()) {
78+
$boolValue = true;
79+
} elseif ($argType->isFalse()->yes()) {
80+
$boolValue = false;
81+
} else {
7682
throw new ShouldNotHappenException(
7783
sprintf('Invalid argument provided to asArray method at line %d', $methodCall->getLine()),
7884
);
7985
}
8086

81-
return new ActiveQueryObjectType($calledOnType->getModelClass(), $argType->getValue());
87+
return new ActiveQueryObjectType($calledOnType->getModelClass(), $boolValue);
8288
}
8389

8490
if (!in_array($methodName, ['one', 'all'], true)) {
@@ -90,15 +96,15 @@ public function getTypeFromMethodCall(
9096
new NullType(),
9197
$calledOnType->isAsArray()
9298
? new ArrayType(new StringType(), new MixedType())
93-
: new ActiveRecordObjectType($calledOnType->getModelClass())
99+
: new ActiveRecordObjectType($calledOnType->getModelClass()),
94100
);
95101
}
96102

97103
return new ArrayType(
98104
new IntegerType(),
99105
$calledOnType->isAsArray()
100106
? new ArrayType(new StringType(), new MixedType())
101-
: new ActiveRecordObjectType($calledOnType->getModelClass())
107+
: new ActiveRecordObjectType($calledOnType->getModelClass()),
102108
);
103109
}
104110
}

src/Type/ActiveRecordDynamicMethodReturnTypeExtension.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use PHPStan\Analyser\Scope;
1010
use PHPStan\Reflection\MethodReflection;
1111
use PHPStan\ShouldNotHappenException;
12-
use PHPStan\Type\Constant\ConstantStringType;
1312
use PHPStan\Type\DynamicMethodReturnTypeExtension;
1413
use PHPStan\Type\Type;
1514
use yii\db\ActiveRecord;
@@ -35,7 +34,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
3534
public function getTypeFromMethodCall(
3635
MethodReflection $methodReflection,
3736
MethodCall $methodCall,
38-
Scope $scope
37+
Scope $scope,
3938
): Type {
4039
$arg = $methodCall->args[0];
4140

@@ -45,22 +44,23 @@ public function getTypeFromMethodCall(
4544
'Unexpected arg %s during method call %s at line %d',
4645
get_class($arg),
4746
$methodReflection->getName(),
48-
$methodCall->getLine()
47+
$methodCall->getLine(),
4948
),
5049
);
5150
}
5251

5352
$argType = $scope->getType($arg->value);
54-
if (!$argType instanceof ConstantStringType) {
53+
$constantStrings = $argType->getConstantStrings();
54+
if (count($constantStrings) === 0) {
5555
throw new ShouldNotHappenException(
5656
sprintf(
5757
'Invalid argument provided to method %s' . PHP_EOL .
5858
'Hint: You should use ::class instead of ::className()',
59-
$methodReflection->getName()
60-
)
59+
$methodReflection->getName(),
60+
),
6161
);
6262
}
6363

64-
return new ActiveQueryObjectType($argType->getValue(), false);
64+
return new ActiveQueryObjectType($constantStrings[0]->getValue(), false);
6565
}
6666
}

src/Type/ActiveRecordDynamicStaticMethodReturnTypeExtension.php

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,27 @@
99
use PHPStan\Analyser\Scope;
1010
use PHPStan\Reflection\MethodReflection;
1111
use PHPStan\Reflection\ParametersAcceptorSelector;
12+
use PHPStan\Reflection\ReflectionProvider;
1213
use PHPStan\ShouldNotHappenException;
1314
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
1415
use PHPStan\Type\NullType;
15-
use PHPStan\Type\ObjectType;
1616
use PHPStan\Type\ThisType;
1717
use PHPStan\Type\Type;
1818
use PHPStan\Type\TypeCombinator;
1919
use PHPStan\Type\UnionType;
2020
use yii\db\ActiveQuery;
2121
use yii\db\ActiveRecord;
2222

23-
use function is_a;
24-
2523
final class ActiveRecordDynamicStaticMethodReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
2624
{
25+
private ReflectionProvider $reflectionProvider;
26+
27+
public function __construct(
28+
ReflectionProvider $reflectionProvider,
29+
) {
30+
$this->reflectionProvider = $reflectionProvider;
31+
}
32+
2733
public function getClass(): string
2834
{
2935
return ActiveRecord::class;
@@ -34,33 +40,52 @@ public function getClass(): string
3440
*/
3541
public function isStaticMethodSupported(MethodReflection $methodReflection): bool
3642
{
37-
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
43+
$variants = $methodReflection->getVariants();
44+
if (count($variants) === 0) {
45+
return false;
46+
}
47+
48+
$returnType = $variants[0]->getReturnType();
3849
if ($returnType instanceof ThisType) {
3950
return true;
4051
}
4152

4253
if ($returnType instanceof UnionType) {
4354
foreach ($returnType->getTypes() as $type) {
44-
if ($type instanceof ObjectType) {
45-
return is_a($type->getClassName(), $this->getClass(), true);
55+
$classNames = $type->getObjectClassNames();
56+
if (count($classNames) > 0) {
57+
$className = $classNames[0];
58+
if ($this->reflectionProvider->hasClass($className)) {
59+
$classReflection = $this->reflectionProvider->getClass($className);
60+
return $classReflection->isSubclassOf($this->getClass());
61+
}
4662
}
4763
}
4864
}
4965

50-
return $returnType instanceof ObjectType &&
51-
is_a($returnType->getClassName(), ActiveQuery::class, true);
66+
$classNames = $returnType->getObjectClassNames();
67+
if (count($classNames) > 0) {
68+
$className = $classNames[0];
69+
if ($this->reflectionProvider->hasClass($className)) {
70+
$classReflection = $this->reflectionProvider->getClass($className);
71+
return $classReflection->isSubclassOf(ActiveQuery::class);
72+
}
73+
}
74+
75+
return false;
5276
}
5377

54-
/**
55-
* @throws ShouldNotHappenException
56-
*/
5778
public function getTypeFromStaticMethodCall(
5879
MethodReflection $methodReflection,
5980
StaticCall $methodCall,
60-
Scope $scope
81+
Scope $scope,
6182
): Type {
6283
$className = $methodCall->class;
63-
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
84+
$returnType = ParametersAcceptorSelector::selectFromArgs(
85+
$scope,
86+
$methodCall->getArgs(),
87+
$methodReflection->getVariants(),
88+
)->getReturnType();
6489

6590
if (!$className instanceof Name) {
6691
return $returnType;
@@ -75,7 +100,7 @@ public function getTypeFromStaticMethodCall(
75100
if ($returnType instanceof UnionType) {
76101
return TypeCombinator::union(
77102
new NullType(),
78-
new ActiveRecordObjectType($name)
103+
new ActiveRecordObjectType($name),
79104
);
80105
}
81106

src/Type/ActiveRecordObjectType.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use ArrayAccess;
88
use PHPStan\ShouldNotHappenException;
99
use PHPStan\TrinaryLogic;
10-
use PHPStan\Type\Constant\ConstantStringType;
1110
use PHPStan\Type\ErrorType;
1211
use PHPStan\Type\ObjectType;
1312
use PHPStan\Type\Type;
@@ -19,20 +18,22 @@ final class ActiveRecordObjectType extends ObjectType
1918
*/
2019
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
2120
{
22-
if (!$offsetType instanceof ConstantStringType) {
21+
$constantStrings = $offsetType->getConstantStrings();
22+
if (count($constantStrings) === 0) {
2323
return TrinaryLogic::createNo();
2424
}
2525

2626
if ($this->isInstanceOf(ArrayAccess::class)->yes()) {
27-
return TrinaryLogic::createFromBoolean($this->hasProperty($offsetType->getValue())->yes());
27+
return TrinaryLogic::createFromBoolean($this->hasProperty($constantStrings[0]->getValue())->yes());
2828
}
2929

3030
return parent::hasOffsetValueType($offsetType);
3131
}
3232

3333
public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
3434
{
35-
if ($offsetType instanceof ConstantStringType && $this->hasProperty($offsetType->getValue())->no()) {
35+
$constantStrings = $offsetType->getConstantStrings();
36+
if (count($constantStrings) > 0 && $this->hasProperty($constantStrings[0]->getValue())->no()) {
3637
return new ErrorType();
3738
}
3839

src/Type/ContainerDynamicMethodReturnTypeExtension.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
4242
}
4343
}
4444

45-
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
45+
return ParametersAcceptorSelector::selectFromArgs(
46+
$scope,
47+
$methodCall->getArgs(),
48+
$methodReflection->getVariants(),
49+
)->getReturnType();
4650
}
4751
}

src/Type/HeaderCollectionDynamicMethodReturnTypeExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
4545
/** @var Arg $arg */
4646
$arg = $methodCall->args[2];
4747
if ($arg->value instanceof ConstFetch) {
48-
$value = $arg->value->name->parts[0];
48+
$value = $arg->value->name->getParts()[0];
4949
if ($value === 'true') {
5050
// $first === true, therefore string
5151
return new StringType();

tests/ServiceMapTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function testThrowExceptionWhenServiceHasUnsupportedType(): void
4545
$this->expectExceptionMessage('Unsupported service definition for unsupported-type');
4646

4747
new ServiceMap(
48-
__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'yii-config-invalid-unsupported-type.php'
48+
__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'yii-config-invalid-unsupported-type.php',
4949
);
5050
}
5151

@@ -58,7 +58,7 @@ public function testThrowExceptionWhenServiceHasUnsupportedArray(): void
5858
$this->expectExceptionMessage('Cannot guess service definition for unsupported-array');
5959

6060
new ServiceMap(
61-
__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'yii-config-invalid-unsupported-array.php'
61+
__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'yii-config-invalid-unsupported-array.php',
6262
);
6363
}
6464

@@ -71,7 +71,7 @@ public function testThrowExceptionWhenComponentHasInvalidValue(): void
7171
$this->expectExceptionMessage('Invalid value for component with id customComponent. Expected object or array.');
7272

7373
new ServiceMap(
74-
__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'yii-config-invalid-component.php'
74+
__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'yii-config-invalid-component.php',
7575
);
7676
}
7777

0 commit comments

Comments
 (0)