From 4c3799adb3e422ee14dd46f745a6148045d21947 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 30 Sep 2019 11:52:33 +0200 Subject: [PATCH] [CodingStyle] Allow ctor override for PHP 7.2 in MakeInheritedMethodVisibilitySameAsParentRector --- ...itedMethodVisibilitySameAsParentRector.php | 76 +++++++++++++++++++ .../Fixture/skip_static_ctor.php.inc | 22 ++++++ ...SkipParentConstructOverrideInPHP72Test.php | 36 +++++++++ 3 files changed, 134 insertions(+) create mode 100644 packages/CodingStyle/tests/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_static_ctor.php.inc create mode 100644 packages/CodingStyle/tests/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/SkipParentConstructOverrideInPHP72Test.php diff --git a/packages/CodingStyle/src/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector.php b/packages/CodingStyle/src/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector.php index 8b701f08fc89..5ec20206a59d 100644 --- a/packages/CodingStyle/src/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector.php +++ b/packages/CodingStyle/src/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector.php @@ -3,7 +3,9 @@ namespace Rector\CodingStyle\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Expr\New_; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Return_; use PHPStan\Analyser\Scope; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Rector\AbstractRector; @@ -12,6 +14,8 @@ use ReflectionMethod; /** + * @see https://3v4l.org/RFYmn + * * @see \Rector\CodingStyle\Tests\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector\MakeInheritedMethodVisibilitySameAsParentRectorTest */ final class MakeInheritedMethodVisibilitySameAsParentRector extends AbstractRector @@ -93,6 +97,10 @@ public function refactor(Node $node): ?Node return null; } + if ($this->isConstructorWithStaticFactory($node, $methodName)) { + return null; + } + $this->changeClassMethodVisibilityBasedOnReflectionMethod($node, $parentReflectionMethod); return $node; @@ -139,4 +147,72 @@ private function changeClassMethodVisibilityBasedOnReflectionMethod( return; } } + + /** + * Parent constructor visibility override is allowed only since PHP 7.2+ + * @see https://3v4l.org/RFYmn + */ + private function isConstructorWithStaticFactory(ClassMethod $classMethod, string $methodName): bool + { + if (! $this->isAtLeastPhpVersion('7.2')) { + return false; + } + + if ($methodName !== '__construct') { + return false; + } + + /** @var Node\Stmt\Class_|null $class */ + $class = $classMethod->getAttribute(AttributeKey::CLASS_NODE); + if ($class === null) { + return false; + } + + foreach ($class->getMethods() as $iteratedClassMethod) { + if (! $iteratedClassMethod->isPublic()) { + continue; + } + + if (! $iteratedClassMethod->isStatic()) { + continue; + } + + $isStaticSelfFactory = $this->isStaticSelfFactory($iteratedClassMethod); + + if ($isStaticSelfFactory === false) { + continue; + } + + return true; + } + + return false; + } + + /** + * Looks for: + * public static someMethod() { return new self(); } + */ + private function isStaticSelfFactory(ClassMethod $classMethod): bool + { + if (! $classMethod->isPublic()) { + return false; + } + + if (! $classMethod->isStatic()) { + return false; + } + + return (bool) $this->betterNodeFinder->findFirst($classMethod, function (Node $node): bool { + if (! $node instanceof Return_) { + return false; + } + + if (! $node->expr instanceof New_) { + return false; + } + + return $this->isName($node->expr->class, 'self'); + }); + } } diff --git a/packages/CodingStyle/tests/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_static_ctor.php.inc b/packages/CodingStyle/tests/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_static_ctor.php.inc new file mode 100644 index 000000000000..6977c1894eb4 --- /dev/null +++ b/packages/CodingStyle/tests/Rector/ClassMethod/MakeInheritedMethodVisibilitySameAsParentRector/Fixture/skip_static_ctor.php.inc @@ -0,0 +1,22 @@ += 7.2 + * @see https://phpunit.readthedocs.io/en/8.3/incomplete-and-skipped-tests.html#incomplete-and-skipped-tests-requires-tables-api + * + * @dataProvider provideDataForTest() + */ + public function test(string $file): void + { + $this->doTestFile($file); + } + + public function provideDataForTest(): Iterator + { + yield [__DIR__ . '/Fixture/skip_static_ctor.php.inc']; + } + + protected function getRectorClass(): string + { + return MakeInheritedMethodVisibilitySameAsParentRector::class; + } + + protected function getPhpVersion(): string + { + return '7.2'; + } +}