Skip to content
Closed
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
1 change: 1 addition & 0 deletions rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ rules:
- PHPStan\Rules\DisallowedConstructs\DisallowedImplicitArrayCreationRule
- PHPStan\Rules\DisallowedConstructs\DisallowedShortTernaryRule
- PHPStan\Rules\ForeachLoop\OverwriteVariablesWithForeachRule
- PHPStan\Rules\Methods\MethodVisibilityOverrideRule
- PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule
- PHPStan\Rules\Operators\OperandInArithmeticPostDecrementRule
- PHPStan\Rules\Operators\OperandInArithmeticPostIncrementRule
Expand Down
59 changes: 59 additions & 0 deletions src/Rules/Methods/MethodVisibilityOverrideRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Methods;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassMethodNode;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Rules\Rule;

/**
* @implements Rule<InClassMethodNode>
*/
class MethodVisibilityOverrideRule implements Rule
{

public function getNodeType(): string
{
return InClassMethodNode::class;
}

public function processNode(Node $node, Scope $scope): array
{
$methodReflection = $scope->getFunction();
if (!$methodReflection instanceof MethodReflection) {
return [];
}

$methodName = $methodReflection->getName();
if (strtolower($methodName) === '__construct') {
return [];
}

$declaringClass = $methodReflection->getDeclaringClass();
$parentClass = $declaringClass->getParentClass();

if ($parentClass === false) {
return [];
}

if (!$parentClass->hasNativeMethod($methodName)) {
return [];
}

$parentMethodReflection = $parentClass->getNativeMethod($methodName);

if (!$parentMethodReflection->isPrivate() && !$parentMethodReflection->isPublic() && $methodReflection->isPublic()) {
$message = sprintf(
'Method %s::%s() overrides visibility from protected to public',
$declaringClass->getDisplayName(),
$methodName,
);
return [$message];
}

return [];
}

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

namespace PHPStan\Rules\Methods;

class MethodVisibilityOverrideRuleTest extends \PHPStan\Testing\RuleTestCase
{

protected function getRule(): \PHPStan\Rules\Rule
{
return new MethodVisibilityOverrideRule();
}

public function testRule(): void
{
$this->analyse([__DIR__ . '/data/visibility-override.php'], [
[
'Method MethodVisibilityOverride\\SubClass::foo3() overrides visibility from protected to public',
63,
],
[
'Method MethodVisibilityOverride\\OtherSubSubClass::foo3() overrides visibility from protected to public',
93,
],
]);
}

}
98 changes: 98 additions & 0 deletions tests/Rules/Methods/data/visibility-override.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

namespace MethodVisibilityOverride;

class BaseClass
{

protected function __construct()
{

}

public function foo1()
{

}

protected function foo2()
{

}

protected function foo3()
{

}

private function foo4()
{

}

private function foo5()
{

}

private function foo6()
{

}

}

class SubClass extends BaseClass
{

public function __construct()
{

}

public function foo1()
{

}

protected function foo2()
{

}

public function foo3()
{

}

public function foo4()
{

}

protected function foo5()
{

}

protected function foo6()
{

}

}

class OtherSubClass extends BaseClass
{

}

class OtherSubSubClass extends OtherSubClass
{

public function foo3()
{

}

}