Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,9 @@ services:
-
class: PHPStan\Rules\Methods\MethodParameterComparisonHelper

-
class: PHPStan\Rules\Methods\MethodVisibilityComparisonHelper

-
class: PHPStan\Rules\MissingTypehintCheck
arguments:
Expand Down
11 changes: 10 additions & 1 deletion src/PhpDoc/StubValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
use PHPStan\Rules\Methods\ExistingClassesInTypehintsRule;
use PHPStan\Rules\Methods\MethodParameterComparisonHelper;
use PHPStan\Rules\Methods\MethodSignatureRule;
use PHPStan\Rules\Methods\MethodVisibilityComparisonHelper;
use PHPStan\Rules\Methods\MissingMethodParameterTypehintRule;
use PHPStan\Rules\Methods\MissingMethodReturnTypehintRule;
use PHPStan\Rules\Methods\MissingMethodSelfOutTypeRule;
Expand Down Expand Up @@ -209,7 +210,15 @@ private function getRuleRegistry(Container $container): RuleRegistry
new ExistingClassesInTypehintsRule($functionDefinitionCheck),
new \PHPStan\Rules\Functions\ExistingClassesInTypehintsRule($functionDefinitionCheck),
new ExistingClassesInPropertiesRule($reflectionProvider, $classNameCheck, $unresolvableTypeHelper, $phpVersion, true, false),
new OverridingMethodRule($phpVersion, new MethodSignatureRule($phpClassReflectionExtension, true, true), true, new MethodParameterComparisonHelper($phpVersion), $phpClassReflectionExtension, $container->getParameter('checkMissingOverrideMethodAttribute')),
new OverridingMethodRule(
$phpVersion,
new MethodSignatureRule($phpClassReflectionExtension, true, true),
true,
new MethodParameterComparisonHelper($phpVersion),
new MethodVisibilityComparisonHelper(),
$phpClassReflectionExtension,
$container->getParameter('checkMissingOverrideMethodAttribute'),
),
new DuplicateDeclarationRule(),
new LocalTypeAliasesRule($localTypeAliasesCheck),
new LocalTypeTraitAliasesRule($localTypeAliasesCheck, $reflectionProvider),
Expand Down
7 changes: 6 additions & 1 deletion src/Rules/Methods/ConsistentConstructorRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\Node\InClassMethodNode;
use PHPStan\Reflection\Dummy\DummyConstructorReflection;
use PHPStan\Rules\Rule;
use function array_merge;
use function strtolower;

/** @implements Rule<InClassMethodNode> */
Expand All @@ -15,6 +16,7 @@ final class ConsistentConstructorRule implements Rule

public function __construct(
private MethodParameterComparisonHelper $methodParameterComparisonHelper,
private MethodVisibilityComparisonHelper $methodVisibilityComparisonHelper,
)
{
}
Expand Down Expand Up @@ -47,7 +49,10 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

return $this->methodParameterComparisonHelper->compare($parentConstructor, $parentConstructor->getDeclaringClass(), $method, true);
return array_merge(
$this->methodParameterComparisonHelper->compare($parentConstructor, $parentConstructor->getDeclaringClass(), $method, true),
$this->methodVisibilityComparisonHelper->compare($parentConstructor, $parentConstructor->getDeclaringClass(), $method),
);
}

}
51 changes: 51 additions & 0 deletions src/Rules/Methods/MethodVisibilityComparisonHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Methods;

use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ExtendedMethodReflection;
use PHPStan\Reflection\Php\PhpMethodFromParserNodeReflection;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;

final class MethodVisibilityComparisonHelper
{

/** @return list<IdentifierRuleError> */
public function compare(ExtendedMethodReflection $prototype, ClassReflection $prototypeDeclaringClass, PhpMethodFromParserNodeReflection $method): array
{
/** @var list<IdentifierRuleError> $messages */
$messages = [];

if ($prototype->isPublic()) {
if (!$method->isPublic()) {
$messages[] = RuleErrorBuilder::message(sprintf(
'%s method %s::%s() overriding public method %s::%s() should also be public.',
$method->isPrivate() ? 'Private' : 'Protected',
$method->getDeclaringClass()->getDisplayName(),
$method->getName(),
$prototypeDeclaringClass->getDisplayName(true),
$prototype->getName(),
))
->nonIgnorable()
->identifier('method.visibility')
->build();
}
} elseif ($method->isPrivate()) {
$messages[] = RuleErrorBuilder::message(sprintf(
'Private method %s::%s() overriding protected method %s::%s() should be protected or public.',
$method->getDeclaringClass()->getDisplayName(),
$method->getName(),
$prototypeDeclaringClass->getDisplayName(true),
$prototype->getName(),
))
->nonIgnorable()
->identifier('method.visibility')
->build();
}

return $messages;
}

}
28 changes: 2 additions & 26 deletions src/Rules/Methods/OverridingMethodRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public function __construct(
private MethodSignatureRule $methodSignatureRule,
private bool $checkPhpDocMethodSignatures,
private MethodParameterComparisonHelper $methodParameterComparisonHelper,
private MethodVisibilityComparisonHelper $methodVisibilityComparisonHelper,
private PhpClassReflectionExtension $phpClassReflectionExtension,
private bool $checkMissingOverrideMethodAttribute,
)
Expand Down Expand Up @@ -165,32 +166,7 @@ public function processNode(Node $node, Scope $scope): array
}

if ($checkVisibility) {
if ($prototype->isPublic()) {
if (!$method->isPublic()) {
$messages[] = RuleErrorBuilder::message(sprintf(
'%s method %s::%s() overriding public method %s::%s() should also be public.',
$method->isPrivate() ? 'Private' : 'Protected',
$method->getDeclaringClass()->getDisplayName(),
$method->getName(),
$prototypeDeclaringClass->getDisplayName(true),
$prototype->getName(),
))
->nonIgnorable()
->identifier('method.visibility')
->build();
}
} elseif ($method->isPrivate()) {
$messages[] = RuleErrorBuilder::message(sprintf(
'Private method %s::%s() overriding protected method %s::%s() should be protected or public.',
$method->getDeclaringClass()->getDisplayName(),
$method->getName(),
$prototypeDeclaringClass->getDisplayName(true),
$prototype->getName(),
))
->nonIgnorable()
->identifier('method.visibility')
->build();
}
$messages = array_merge($messages, $this->methodVisibilityComparisonHelper->compare($prototype, $prototypeDeclaringClass, $method));
}

$prototypeVariants = $prototype->getVariants();
Expand Down
15 changes: 14 additions & 1 deletion tests/PHPStan/Rules/Methods/ConsistentConstructorRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ class ConsistentConstructorRuleTest extends RuleTestCase

protected function getRule(): Rule
{
return new ConsistentConstructorRule(self::getContainer()->getByType(MethodParameterComparisonHelper::class));
return new ConsistentConstructorRule(
self::getContainer()->getByType(MethodParameterComparisonHelper::class),
self::getContainer()->getByType(MethodVisibilityComparisonHelper::class),
);
}

public function testRule(): void
Expand Down Expand Up @@ -42,4 +45,14 @@ public function testRuleNoErrors(): void
$this->analyse([__DIR__ . '/data/consistent-constructor-no-errors.php'], []);
}

public function testBug12137(): void
{
$this->analyse([__DIR__ . '/data/bug-12137.php'], [
[
'Private method Bug12137\ChildClass::__construct() overriding protected method Bug12137\ParentClass::__construct() should be protected or public.',
20,
],
]);
}

}
1 change: 1 addition & 0 deletions tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ protected function getRule(): Rule
new MethodSignatureRule($phpClassReflectionExtension, $this->reportMaybes, $this->reportStatic),
true,
new MethodParameterComparisonHelper($phpVersion),
new MethodVisibilityComparisonHelper(),
$phpClassReflectionExtension,
false,
);
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Rules/Methods/OverridingMethodRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ protected function getRule(): Rule
new MethodSignatureRule($phpClassReflectionExtension, true, true),
false,
new MethodParameterComparisonHelper($phpVersion),
new MethodVisibilityComparisonHelper(),
$phpClassReflectionExtension,
$this->checkMissingOverrideMethodAttribute,
);
Expand Down
23 changes: 23 additions & 0 deletions tests/PHPStan/Rules/Methods/data/bug-12137.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types = 1);

namespace Bug12137;

/** @phpstan-consistent-constructor */
abstract class ParentClass
{
protected function __construct()
{
}

public static function create(): static
{
return new static();
}
}

class ChildClass extends ParentClass
{
private function __construct()
{
}
}
Loading