Skip to content

Commit ca55346

Browse files
authored
Error on references of prefixed internal class names from PHAR files
1 parent 3f31056 commit ca55346

File tree

65 files changed

+719
-188
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+719
-188
lines changed

conf/config.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,11 +833,17 @@ services:
833833
reportMaybes: %reportMaybes%
834834
bleedingEdge: %featureToggles.bleedingEdge%
835835

836+
-
837+
class: PHPStan\Rules\ClassNameCheck
838+
836839
-
837840
class: PHPStan\Rules\ClassCaseSensitivityCheck
838841
arguments:
839842
checkInternalClassCaseSensitivity: %checkInternalClassCaseSensitivity%
840843

844+
-
845+
class: PHPStan\Rules\ClassForbiddenNameCheck
846+
841847
-
842848
class: PHPStan\Rules\Classes\LocalTypeAliasesCheck
843849
arguments:

src/PhpDoc/StubValidator.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use PHPStan\Reflection\PhpVersionStaticAccessor;
1717
use PHPStan\Reflection\ReflectionProvider;
1818
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
19-
use PHPStan\Rules\ClassCaseSensitivityCheck;
2019
use PHPStan\Rules\Classes\DuplicateClassDeclarationRule;
2120
use PHPStan\Rules\Classes\DuplicateDeclarationRule;
2221
use PHPStan\Rules\Classes\ExistingClassesInClassImplementsRule;
@@ -26,6 +25,7 @@
2625
use PHPStan\Rules\Classes\LocalTypeAliasesCheck;
2726
use PHPStan\Rules\Classes\LocalTypeAliasesRule;
2827
use PHPStan\Rules\Classes\LocalTypeTraitAliasesRule;
28+
use PHPStan\Rules\ClassNameCheck;
2929
use PHPStan\Rules\DirectRegistry as DirectRuleRegistry;
3030
use PHPStan\Rules\FunctionDefinitionCheck;
3131
use PHPStan\Rules\Functions\DuplicateFunctionDeclarationRule;
@@ -148,7 +148,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
148148
$templateTypeCheck = $container->getByType(TemplateTypeCheck::class);
149149
$varianceCheck = $container->getByType(VarianceCheck::class);
150150
$reflectionProvider = $container->getByType(ReflectionProvider::class);
151-
$classCaseSensitivityCheck = $container->getByType(ClassCaseSensitivityCheck::class);
151+
$classNameCheck = $container->getByType(ClassNameCheck::class);
152152
$functionDefinitionCheck = $container->getByType(FunctionDefinitionCheck::class);
153153
$missingTypehintCheck = $container->getByType(MissingTypehintCheck::class);
154154
$unresolvableTypeHelper = $container->getByType(UnresolvableTypeHelper::class);
@@ -159,13 +159,13 @@ private function getRuleRegistry(Container $container): RuleRegistry
159159

160160
$rules = [
161161
// level 0
162-
new ExistingClassesInClassImplementsRule($classCaseSensitivityCheck, $reflectionProvider),
163-
new ExistingClassesInInterfaceExtendsRule($classCaseSensitivityCheck, $reflectionProvider),
164-
new ExistingClassInClassExtendsRule($classCaseSensitivityCheck, $reflectionProvider),
165-
new ExistingClassInTraitUseRule($classCaseSensitivityCheck, $reflectionProvider),
162+
new ExistingClassesInClassImplementsRule($classNameCheck, $reflectionProvider),
163+
new ExistingClassesInInterfaceExtendsRule($classNameCheck, $reflectionProvider),
164+
new ExistingClassInClassExtendsRule($classNameCheck, $reflectionProvider),
165+
new ExistingClassInTraitUseRule($classNameCheck, $reflectionProvider),
166166
new ExistingClassesInTypehintsRule($functionDefinitionCheck),
167167
new \PHPStan\Rules\Functions\ExistingClassesInTypehintsRule($functionDefinitionCheck),
168-
new ExistingClassesInPropertiesRule($reflectionProvider, $classCaseSensitivityCheck, $unresolvableTypeHelper, $phpVersion, true, false),
168+
new ExistingClassesInPropertiesRule($reflectionProvider, $classNameCheck, $unresolvableTypeHelper, $phpVersion, true, false),
169169
new OverridingMethodRule($phpVersion, new MethodSignatureRule($phpClassReflectionExtension, true, true, $container->getParameter('featureToggles')['abstractTraitMethod']), true, new MethodParameterComparisonHelper($phpVersion, $container->getParameter('featureToggles')['genericPrototypeMessage']), $phpClassReflectionExtension, $container->getParameter('featureToggles')['genericPrototypeMessage'], $container->getParameter('featureToggles')['finalByPhpDoc'], $container->getParameter('checkMissingOverrideMethodAttribute')),
170170
new DuplicateDeclarationRule(),
171171
new LocalTypeAliasesRule($localTypeAliasesCheck),

src/Rules/AttributesCheck.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class AttributesCheck
2020
public function __construct(
2121
private ReflectionProvider $reflectionProvider,
2222
private FunctionCallParametersCheck $functionCallParametersCheck,
23-
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
23+
private ClassNameCheck $classCheck,
2424
private bool $deprecationRulesInstalled,
2525
)
2626
{
@@ -67,7 +67,7 @@ public function check(
6767
$errors[] = RuleErrorBuilder::message(sprintf('Attribute class %s is abstract.', $name))->line($attribute->getLine())->build();
6868
}
6969

70-
foreach ($this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($name, $attribute)]) as $caseSensitivityError) {
70+
foreach ($this->classCheck->checkClassNames([new ClassNameNodePair($name, $attribute)]) as $caseSensitivityError) {
7171
$errors[] = $caseSensitivityError;
7272
}
7373

src/Rules/ClassForbiddenNameCheck.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules;
4+
5+
use function sprintf;
6+
use function str_starts_with;
7+
use function strpos;
8+
use function substr;
9+
10+
class ClassForbiddenNameCheck
11+
{
12+
13+
private const INTERNAL_CLASS_PREFIXES = [
14+
'PHPStan' => '_PHPStan_',
15+
'Rector' => 'RectorPrefix',
16+
'PHP-Scoper' => '_PhpScoper',
17+
];
18+
19+
/**
20+
* @param ClassNameNodePair[] $pairs
21+
* @return RuleError[]
22+
*/
23+
public function checkClassNames(array $pairs): array
24+
{
25+
$errors = [];
26+
foreach ($pairs as $pair) {
27+
$className = $pair->getClassName();
28+
29+
$projectName = null;
30+
foreach (self::INTERNAL_CLASS_PREFIXES as $project => $prefix) {
31+
if (str_starts_with($className, $prefix)) {
32+
$projectName = $project;
33+
break;
34+
}
35+
}
36+
37+
if ($projectName === null) {
38+
continue;
39+
}
40+
41+
$error = RuleErrorBuilder::message(sprintf(
42+
'Referencing prefixed %s class: %s.',
43+
$projectName,
44+
$className,
45+
))->line($pair->getNode()->getLine())->nonIgnorable();
46+
47+
if (strpos($className, '\\') !== false) {
48+
$error->tip(sprintf(
49+
'This is most likely unintentional. Did you mean to type %s?',
50+
substr($className, strpos($className, '\\')),
51+
));
52+
}
53+
54+
$errors[] = $error->build();
55+
}
56+
57+
return $errors;
58+
}
59+
60+
}

src/Rules/ClassNameCheck.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules;
4+
5+
class ClassNameCheck
6+
{
7+
8+
public function __construct(
9+
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
10+
private ClassForbiddenNameCheck $classForbiddenNameCheck,
11+
)
12+
{
13+
}
14+
15+
/**
16+
* @param ClassNameNodePair[] $pairs
17+
* @return RuleError[]
18+
*/
19+
public function checkClassNames(array $pairs, bool $checkClassCaseSensitivity = true): array
20+
{
21+
$errors = [];
22+
23+
if ($checkClassCaseSensitivity) {
24+
foreach ($this->classCaseSensitivityCheck->checkClassNames($pairs) as $error) {
25+
$errors[] = $error;
26+
}
27+
}
28+
foreach ($this->classForbiddenNameCheck->checkClassNames($pairs) as $error) {
29+
$errors[] = $error;
30+
}
31+
32+
return $errors;
33+
}
34+
35+
}

src/Rules/Classes/ClassConstantRule.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use PHPStan\Internal\SprintfHelper;
1010
use PHPStan\Php\PhpVersion;
1111
use PHPStan\Reflection\ReflectionProvider;
12-
use PHPStan\Rules\ClassCaseSensitivityCheck;
12+
use PHPStan\Rules\ClassNameCheck;
1313
use PHPStan\Rules\ClassNameNodePair;
1414
use PHPStan\Rules\Rule;
1515
use PHPStan\Rules\RuleErrorBuilder;
@@ -34,7 +34,7 @@ class ClassConstantRule implements Rule
3434
public function __construct(
3535
private ReflectionProvider $reflectionProvider,
3636
private RuleLevelHelper $ruleLevelHelper,
37-
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
37+
private ClassNameCheck $classCheck,
3838
private PhpVersion $phpVersion,
3939
)
4040
{
@@ -101,7 +101,7 @@ public function processNode(Node $node, Scope $scope): array
101101
];
102102
}
103103

104-
$messages = $this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($className, $class)]);
104+
$messages = $this->classCheck->checkClassNames([new ClassNameNodePair($className, $class)]);
105105

106106
$classType = $scope->resolveTypeByName($class);
107107
}

src/Rules/Classes/ExistingClassInClassExtendsRule.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Reflection\ReflectionProvider;
8-
use PHPStan\Rules\ClassCaseSensitivityCheck;
8+
use PHPStan\Rules\ClassNameCheck;
99
use PHPStan\Rules\ClassNameNodePair;
1010
use PHPStan\Rules\Rule;
1111
use PHPStan\Rules\RuleErrorBuilder;
@@ -18,7 +18,7 @@ class ExistingClassInClassExtendsRule implements Rule
1818
{
1919

2020
public function __construct(
21-
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
21+
private ClassNameCheck $classCheck,
2222
private ReflectionProvider $reflectionProvider,
2323
)
2424
{
@@ -35,7 +35,7 @@ public function processNode(Node $node, Scope $scope): array
3535
return [];
3636
}
3737
$extendedClassName = (string) $node->extends;
38-
$messages = $this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($extendedClassName, $node->extends)]);
38+
$messages = $this->classCheck->checkClassNames([new ClassNameNodePair($extendedClassName, $node->extends)]);
3939
$currentClassName = null;
4040
if (isset($node->namespacedName)) {
4141
$currentClassName = (string) $node->namespacedName;

src/Rules/Classes/ExistingClassInInstanceOfRule.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use PhpParser\Node\Expr\Instanceof_;
77
use PHPStan\Analyser\Scope;
88
use PHPStan\Reflection\ReflectionProvider;
9-
use PHPStan\Rules\ClassCaseSensitivityCheck;
9+
use PHPStan\Rules\ClassNameCheck;
1010
use PHPStan\Rules\ClassNameNodePair;
1111
use PHPStan\Rules\Rule;
1212
use PHPStan\Rules\RuleErrorBuilder;
@@ -24,7 +24,7 @@ class ExistingClassInInstanceOfRule implements Rule
2424

2525
public function __construct(
2626
private ReflectionProvider $reflectionProvider,
27-
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
27+
private ClassNameCheck $classCheck,
2828
private bool $checkClassCaseSensitivity,
2929
)
3030
{
@@ -69,13 +69,16 @@ public function processNode(Node $node, Scope $scope): array
6969
return [
7070
RuleErrorBuilder::message(sprintf('Class %s not found.', $name))->line($class->getLine())->discoveringSymbolsTip()->build(),
7171
];
72-
} elseif ($this->checkClassCaseSensitivity) {
73-
$errors = array_merge(
74-
$errors,
75-
$this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($name, $class)]),
76-
);
7772
}
7873

74+
$errors = array_merge(
75+
$errors,
76+
$this->classCheck->checkClassNames(
77+
[new ClassNameNodePair($name, $class)],
78+
$this->checkClassCaseSensitivity,
79+
),
80+
);
81+
7982
$classReflection = $this->reflectionProvider->getClass($name);
8083

8184
if ($classReflection->isTrait()) {

src/Rules/Classes/ExistingClassInTraitUseRule.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Reflection\ReflectionProvider;
8-
use PHPStan\Rules\ClassCaseSensitivityCheck;
8+
use PHPStan\Rules\ClassNameCheck;
99
use PHPStan\Rules\ClassNameNodePair;
1010
use PHPStan\Rules\Rule;
1111
use PHPStan\Rules\RuleErrorBuilder;
@@ -20,7 +20,7 @@ class ExistingClassInTraitUseRule implements Rule
2020
{
2121

2222
public function __construct(
23-
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
23+
private ClassNameCheck $classCheck,
2424
private ReflectionProvider $reflectionProvider,
2525
)
2626
{
@@ -33,7 +33,7 @@ public function getNodeType(): string
3333

3434
public function processNode(Node $node, Scope $scope): array
3535
{
36-
$messages = $this->classCaseSensitivityCheck->checkClassNames(
36+
$messages = $this->classCheck->checkClassNames(
3737
array_map(static fn (Node\Name $traitName): ClassNameNodePair => new ClassNameNodePair((string) $traitName, $traitName), $node->traits),
3838
);
3939

src/Rules/Classes/ExistingClassesInClassImplementsRule.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Reflection\ReflectionProvider;
8-
use PHPStan\Rules\ClassCaseSensitivityCheck;
8+
use PHPStan\Rules\ClassNameCheck;
99
use PHPStan\Rules\ClassNameNodePair;
1010
use PHPStan\Rules\Rule;
1111
use PHPStan\Rules\RuleErrorBuilder;
@@ -19,7 +19,7 @@ class ExistingClassesInClassImplementsRule implements Rule
1919
{
2020

2121
public function __construct(
22-
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
22+
private ClassNameCheck $classCheck,
2323
private ReflectionProvider $reflectionProvider,
2424
)
2525
{
@@ -32,7 +32,7 @@ public function getNodeType(): string
3232

3333
public function processNode(Node $node, Scope $scope): array
3434
{
35-
$messages = $this->classCaseSensitivityCheck->checkClassNames(
35+
$messages = $this->classCheck->checkClassNames(
3636
array_map(static fn (Node\Name $interfaceName): ClassNameNodePair => new ClassNameNodePair((string) $interfaceName, $interfaceName), $node->implements),
3737
);
3838

src/Rules/Classes/ExistingClassesInEnumImplementsRule.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Reflection\ReflectionProvider;
8-
use PHPStan\Rules\ClassCaseSensitivityCheck;
8+
use PHPStan\Rules\ClassNameCheck;
99
use PHPStan\Rules\ClassNameNodePair;
1010
use PHPStan\Rules\Rule;
1111
use PHPStan\Rules\RuleErrorBuilder;
@@ -19,7 +19,7 @@ class ExistingClassesInEnumImplementsRule implements Rule
1919
{
2020

2121
public function __construct(
22-
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
22+
private ClassNameCheck $classCheck,
2323
private ReflectionProvider $reflectionProvider,
2424
)
2525
{
@@ -32,7 +32,7 @@ public function getNodeType(): string
3232

3333
public function processNode(Node $node, Scope $scope): array
3434
{
35-
$messages = $this->classCaseSensitivityCheck->checkClassNames(
35+
$messages = $this->classCheck->checkClassNames(
3636
array_map(static fn (Node\Name $interfaceName): ClassNameNodePair => new ClassNameNodePair((string) $interfaceName, $interfaceName), $node->implements),
3737
);
3838

src/Rules/Classes/ExistingClassesInInterfaceExtendsRule.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Reflection\ReflectionProvider;
8-
use PHPStan\Rules\ClassCaseSensitivityCheck;
8+
use PHPStan\Rules\ClassNameCheck;
99
use PHPStan\Rules\ClassNameNodePair;
1010
use PHPStan\Rules\Rule;
1111
use PHPStan\Rules\RuleErrorBuilder;
@@ -19,7 +19,7 @@ class ExistingClassesInInterfaceExtendsRule implements Rule
1919
{
2020

2121
public function __construct(
22-
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
22+
private ClassNameCheck $classCheck,
2323
private ReflectionProvider $reflectionProvider,
2424
)
2525
{
@@ -32,7 +32,7 @@ public function getNodeType(): string
3232

3333
public function processNode(Node $node, Scope $scope): array
3434
{
35-
$messages = $this->classCaseSensitivityCheck->checkClassNames(
35+
$messages = $this->classCheck->checkClassNames(
3636
array_map(static fn (Node\Name $interfaceName): ClassNameNodePair => new ClassNameNodePair((string) $interfaceName, $interfaceName), $node->extends),
3737
);
3838

0 commit comments

Comments
 (0)