Skip to content

Commit ac897e8

Browse files
committed
Merge branch 'hostep-road-to-phpstan-2.0'
2 parents 831f8fc + c5a1e99 commit ac897e8

25 files changed

+176
-221
lines changed

composer.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
"php": "^7.2.0 || ^8.1.0",
2424
"ext-dom": "*",
2525
"laminas/laminas-code": "~3.3.0 || ~3.4.1 || ~3.5.1 || ^4.5 || ^4.10",
26-
"phpstan/phpstan": "~1.12.0",
27-
"symfony/finder": "^3.0 || ^4.0 || ^5.0 || ^6.0"
26+
"phpstan/phpstan": "^2.0",
27+
"symfony/finder": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
2828
},
2929
"conflict": {
3030
"magento/framework": "<102.0.0"
@@ -37,10 +37,10 @@
3737
"magento/framework": ">=102.0.0",
3838
"mikey179/vfsstream": "^1.6.10",
3939
"nette/neon": "^3.3.3",
40-
"nikic/php-parser": "^4.13.2",
40+
"nikic/php-parser": "^5.3",
4141
"phpstan/extension-installer": "^1.1.0",
42-
"phpstan/phpstan-phpunit": "^1.1.1",
43-
"phpstan/phpstan-strict-rules": "^1.2.3",
42+
"phpstan/phpstan-phpunit": "^2.0",
43+
"phpstan/phpstan-strict-rules": "^2.0",
4444
"phpunit/phpunit": "^9.5.20",
4545
"squizlabs/php_codesniffer": "^3.6.2"
4646
},

phpstan.neon

+12
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ parameters:
2828
-
2929
message: '~is not covered by backward compatibility promise.~'
3030
path: src/bitExpert/PHPStan/Magento/Reflection/AbstractMagicMethodReflectionExtension.php
31+
-
32+
message: '~PHPDoc tag @var assumes the expression with type~'
33+
path: 'src/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRule.php'
34+
-
35+
message: '~PHPDoc tag @var assumes the expression with type~'
36+
path: 'src/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtension.php'
37+
-
38+
message: '~Function is_subclass_of\(\) is a runtime reflection concept that might not work in PHPStan~'
39+
path: 'src/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtension.php'
3140
-
3241
message: '~is not covered by backward compatibility promise.~'
3342
path: tests/bitExpert/PHPStan/Magento/Autoload/FactoryAutoloaderUnitTest.php
@@ -58,3 +67,6 @@ parameters:
5867
-
5968
message: '~Call to static method PHPUnit\\Framework\\Assert::assertInstanceOf~'
6069
path: tests/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest.php
70+
-
71+
message: '~PHPDoc tag @var assumes the expression with type~'
72+
path: tests/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest.php

src/bitExpert/PHPStan/Magento/Autoload/DataProvider/ClassLoaderProvider.php

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public function __construct(string $magentoRoot)
3030
$this->composer = new ClassLoader($magentoRoot . '/vendor');
3131
$autoloadFile = $magentoRoot . '/vendor/composer/autoload_namespaces.php';
3232
if (is_file($autoloadFile)) {
33+
/** @var array<string, string> $map */
3334
$map = require $autoloadFile;
3435
foreach ($map as $namespace => $path) {
3536
$this->composer->set($namespace, $path);
@@ -38,6 +39,7 @@ public function __construct(string $magentoRoot)
3839

3940
$autoloadFile = $magentoRoot . '/vendor/composer/autoload_psr4.php';
4041
if (is_file($autoloadFile)) {
42+
/** @var array<string, string> $map */
4143
$map = require $autoloadFile;
4244
foreach ($map as $namespace => $path) {
4345
$this->composer->setPsr4($namespace, $path);
@@ -46,6 +48,7 @@ public function __construct(string $magentoRoot)
4648

4749
$autoloadFile = $magentoRoot . '/vendor/composer/autoload_classmap.php';
4850
if (is_file($autoloadFile)) {
51+
/** @var ?array<string, string> $classMap */
4952
$classMap = require $autoloadFile;
5053
if (is_array($classMap)) {
5154
$this->composer->addClassMap($classMap);

src/bitExpert/PHPStan/Magento/Autoload/FactoryAutoloader.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ protected function getFileContents(string $class): string
6969
$namespace = explode('\\', ltrim($class, '\\'));
7070
/** @var string $factoryClassname */
7171
$factoryClassname = array_pop($namespace);
72-
$originalClassname = preg_replace('#Factory$#', '', $factoryClassname);
72+
$originalClassname = preg_replace('#Factory$#', '', $factoryClassname) ?? $factoryClassname;
7373
$namespace = implode('\\', $namespace);
7474

7575
$template = "<?php\n";

src/bitExpert/PHPStan/Magento/Autoload/ProxyAutoloader.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ protected function getFileContents(string $class): string
110110
$defaultValue = ' = false';
111111
break;
112112
default:
113-
$defaultValue = ' = ' . $parameter->getDefaultValue();
113+
if (is_string($parameter->getDefaultValue())) {
114+
$defaultValue = ' = ' . $parameter->getDefaultValue();
115+
}
114116
break;
115117
}
116118
}

src/bitExpert/PHPStan/Magento/Reflection/MagicMethodReflection.php

+2-5
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class MagicMethodReflection implements MethodReflection
2828
*/
2929
private $declaringClass;
3030
/**
31-
* @var ParametersAcceptor[]
31+
* @var list<ParametersAcceptor>
3232
*/
3333
private $variants;
3434

@@ -37,7 +37,7 @@ class MagicMethodReflection implements MethodReflection
3737
*
3838
* @param string $name
3939
* @param ClassReflection $declaringClass
40-
* @param ParametersAcceptor[] $variants
40+
* @param list<ParametersAcceptor> $variants
4141
*/
4242
public function __construct(string $name, ClassReflection $declaringClass, array $variants = [])
4343
{
@@ -76,9 +76,6 @@ public function getPrototype(): ClassMemberReflection
7676
return $this;
7777
}
7878

79-
/**
80-
* @return ParametersAcceptor[]
81-
*/
8279
public function getVariants(): array
8380
{
8481
return $this->variants;

src/bitExpert/PHPStan/Magento/Rules/AbstractModelRetrieveCollectionViaFactoryRule.php

+10-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
use PhpParser\Node\Expr\MethodCall;
1717
use PHPStan\Analyser\Scope;
1818
use PHPStan\Rules\Rule;
19-
use PHPStan\ShouldNotHappenException;
19+
use PHPStan\Rules\RuleError;
20+
use PHPStan\Rules\RuleErrorBuilder;
2021
use PHPStan\Type\ObjectType;
2122
use PHPStan\Type\VerbosityLevel;
2223

@@ -36,18 +37,8 @@ public function getNodeType(): string
3637
return MethodCall::class;
3738
}
3839

39-
/**
40-
* @param Node $node
41-
* @param Scope $scope
42-
* @return (string|\PHPStan\Rules\RuleError)[] errors
43-
* @throws ShouldNotHappenException
44-
*/
4540
public function processNode(Node $node, Scope $scope): array
4641
{
47-
if (!$node instanceof MethodCall) {
48-
throw new ShouldNotHappenException();
49-
}
50-
5142
if (!$node->name instanceof Node\Identifier) {
5243
return [];
5344
}
@@ -63,11 +54,15 @@ public function processNode(Node $node, Scope $scope): array
6354
}
6455

6556
return [
66-
sprintf(
67-
'Collections should be used directly via factory, not via %s::%s() method',
68-
$type->describe(VerbosityLevel::typeOnly()),
69-
$node->name->name
57+
RuleErrorBuilder::message(
58+
sprintf(
59+
'Collections should be used directly via factory, not via %s::%s() method',
60+
$type->describe(VerbosityLevel::typeOnly()),
61+
$node->name->name
62+
)
7063
)
64+
->identifier('bitExpertMagento.abstractModelRetrieveCollectionViaFactory')
65+
->build()
7166
];
7267
}
7368
}

src/bitExpert/PHPStan/Magento/Rules/AbstractModelUseServiceContractRule.php

+10-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
use PhpParser\Node\Expr\MethodCall;
1717
use PHPStan\Analyser\Scope;
1818
use PHPStan\Rules\Rule;
19-
use PHPStan\ShouldNotHappenException;
19+
use PHPStan\Rules\RuleError;
20+
use PHPStan\Rules\RuleErrorBuilder;
2021
use PHPStan\Type\ObjectType;
2122
use PHPStan\Type\VerbosityLevel;
2223

@@ -36,18 +37,8 @@ public function getNodeType(): string
3637
return MethodCall::class;
3738
}
3839

39-
/**
40-
* @param Node $node
41-
* @param Scope $scope
42-
* @return (string|\PHPStan\Rules\RuleError)[] errors
43-
* @throws ShouldNotHappenException
44-
*/
4540
public function processNode(Node $node, Scope $scope): array
4641
{
47-
if (!$node instanceof MethodCall) {
48-
throw new ShouldNotHappenException();
49-
}
50-
5142
if (!$node->name instanceof Node\Identifier) {
5243
return [];
5344
}
@@ -63,11 +54,15 @@ public function processNode(Node $node, Scope $scope): array
6354
}
6455

6556
return [
66-
sprintf(
67-
'Use service contracts to persist entities in favour of %s::%s() method',
68-
$type->describe(VerbosityLevel::typeOnly()),
69-
$node->name->name
57+
RuleErrorBuilder::message(
58+
sprintf(
59+
'Use service contracts to persist entities in favour of %s::%s() method',
60+
$type->describe(VerbosityLevel::typeOnly()),
61+
$node->name->name
62+
)
7063
)
64+
->identifier('bitExpertMagento.abstractModelUseServiceContract')
65+
->build()
7166
];
7267
}
7368
}

src/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRule.php

+10-14
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
use PhpParser\Node\Expr\MethodCall;
1818
use PHPStan\Analyser\Scope;
1919
use PHPStan\Rules\Rule;
20-
use PHPStan\ShouldNotHappenException;
20+
use PHPStan\Rules\RuleError;
21+
use PHPStan\Rules\RuleErrorBuilder;
2122
use PHPStan\Type\Constant\ConstantStringType;
2223
use PHPStan\Type\ErrorType;
2324
use PHPStan\Type\ObjectType;
@@ -38,18 +39,8 @@ public function getNodeType(): string
3839
return MethodCall::class;
3940
}
4041

41-
/**
42-
* @param Node $node
43-
* @param Scope $scope
44-
* @return (string|\PHPStan\Rules\RuleError)[] errors
45-
* @throws ShouldNotHappenException
46-
*/
4742
public function processNode(Node $node, Scope $scope): array
4843
{
49-
if (!$node instanceof MethodCall) {
50-
throw new ShouldNotHappenException();
51-
}
52-
5344
if (!$node->name instanceof Node\Identifier) {
5445
return [];
5546
}
@@ -78,11 +69,16 @@ public function processNode(Node $node, Scope $scope): array
7869
$args = $node->args;
7970
/** @var ConstantStringType $argType */
8071
$argType = $scope->getType($args[0]->value);
72+
8173
return [
82-
sprintf(
83-
'%s does not extend \Magento\Framework\Data\Collection as required!',
84-
$argType->getValue()
74+
RuleErrorBuilder::message(
75+
sprintf(
76+
'%s does not extend \Magento\Framework\Data\Collection as required!',
77+
$argType->getValue()
78+
)
8579
)
80+
->identifier('bitExpertMagento.getCollectionMockMethodNeedsCollectionSubclass')
81+
->build()
8682
];
8783
}
8884
}

src/bitExpert/PHPStan/Magento/Rules/ResourceModelsShouldBeUsedDirectlyRule.php

+10-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
use PhpParser\Node\Expr\MethodCall;
1717
use PHPStan\Analyser\Scope;
1818
use PHPStan\Rules\Rule;
19-
use PHPStan\ShouldNotHappenException;
19+
use PHPStan\Rules\RuleError;
20+
use PHPStan\Rules\RuleErrorBuilder;
2021
use PHPStan\Type\ObjectType;
2122
use PHPStan\Type\VerbosityLevel;
2223

@@ -36,18 +37,8 @@ public function getNodeType(): string
3637
return MethodCall::class;
3738
}
3839

39-
/**
40-
* @param Node $node
41-
* @param Scope $scope
42-
* @return (string|\PHPStan\Rules\RuleError)[] errors
43-
* @throws ShouldNotHappenException
44-
*/
4540
public function processNode(Node $node, Scope $scope): array
4641
{
47-
if (!$node instanceof MethodCall) {
48-
throw new ShouldNotHappenException();
49-
}
50-
5142
if (!$node->name instanceof Node\Identifier) {
5243
return [];
5344
}
@@ -63,11 +54,15 @@ public function processNode(Node $node, Scope $scope): array
6354
}
6455

6556
return [
66-
sprintf(
67-
'%s::%s() is deprecated. Use Resource Models directly',
68-
$type->describe(VerbosityLevel::typeOnly()),
69-
$node->name->name
57+
RuleErrorBuilder::message(
58+
sprintf(
59+
'%s::%s() is deprecated. Use Resource Models directly',
60+
$type->describe(VerbosityLevel::typeOnly()),
61+
$node->name->name
62+
)
7063
)
64+
->identifier('bitExpertMagento.resourceModelsShouldBeUsedDirectly')
65+
->build()
7166
];
7267
}
7368
}

src/bitExpert/PHPStan/Magento/Rules/SetTemplateDisallowedForBlockRule.php

+10-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
use PhpParser\Node\Expr\MethodCall;
1717
use PHPStan\Analyser\Scope;
1818
use PHPStan\Rules\Rule;
19-
use PHPStan\ShouldNotHappenException;
19+
use PHPStan\Rules\RuleError;
20+
use PHPStan\Rules\RuleErrorBuilder;
2021
use PHPStan\Type\ObjectType;
2122
use PHPStan\Type\VerbosityLevel;
2223

@@ -37,18 +38,8 @@ public function getNodeType(): string
3738
return MethodCall::class;
3839
}
3940

40-
/**
41-
* @param Node $node
42-
* @param Scope $scope
43-
* @return (string|\PHPStan\Rules\RuleError)[] errors
44-
* @throws ShouldNotHappenException
45-
*/
4641
public function processNode(Node $node, Scope $scope): array
4742
{
48-
if (!$node instanceof MethodCall) {
49-
throw new ShouldNotHappenException();
50-
}
51-
5243
if (!$node->name instanceof Node\Identifier) {
5344
return [];
5445
}
@@ -64,11 +55,15 @@ public function processNode(Node $node, Scope $scope): array
6455
}
6556

6657
return [
67-
sprintf(
68-
'Setter methods like %s::%s() are deprecated in Block classes, use constructor arguments instead',
69-
$type->describe(VerbosityLevel::typeOnly()),
70-
$node->name->name
58+
RuleErrorBuilder::message(
59+
sprintf(
60+
'Setter methods like %s::%s() are deprecated in Block classes, use constructor arguments instead',
61+
$type->describe(VerbosityLevel::typeOnly()),
62+
$node->name->name
63+
)
7164
)
65+
->identifier('bitExpertMagento.setTemplateDisallowedForBlock')
66+
->build()
7267
];
7368
}
7469
}

src/bitExpert/PHPStan/Magento/Type/ObjectManagerDynamicReturnTypeExtension.php

+8-5
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@
2424

2525
class ObjectManagerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
2626
{
27-
/**
28-
* @return string
29-
*/
3027
public function getClass(): string
3128
{
3229
return 'Magento\Framework\App\ObjectManager';
@@ -64,9 +61,15 @@ public function getTypeFromMethodCall(
6461
/** @var \PhpParser\Node\Arg[] $args */
6562
$args = $methodCall->args;
6663
$argType = $scope->getType($args[0]->value);
67-
if (!$argType instanceof ConstantStringType) {
64+
if ($argType->getConstantStrings() === []) {
6865
return $mixedType;
6966
}
70-
return TypeCombinator::addNull(new ObjectType($argType->getValue()));
67+
68+
$types = [];
69+
foreach ($argType->getConstantStrings() as $constantString) {
70+
$types[] = TypeCombinator::addNull(new ObjectType($constantString->getValue()));
71+
}
72+
73+
return TypeCombinator::union(...$types);
7174
}
7275
}

0 commit comments

Comments
 (0)