Merge pull request #2065 from rectorphp/allow-private-ctor-override-for-static-factory

[CodingStyle] Allow private ctor override for static factory
This commit is contained in:
Tomáš Votruba 2019-10-01 13:44:30 +02:00 committed by GitHub
commit f0222eaebf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 134 additions and 0 deletions

View File

@ -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 Rector\RectorDefinition\RectorDefinition;
use ReflectionMethod;
/**
* @see https://3v4l.org/RFYmn
*
* @see \Rector\CodingStyle\Tests\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector\MakeInheritedMethodVisibilitySameAsParentRectorTest
*/
final class MakeInheritedMethodVisibilitySameAsParentRector extends AbstractRector
@ -93,6 +97,10 @@ PHP
return null;
}
if ($this->isConstructorWithStaticFactory($node, $methodName)) {
return null;
}
$this->changeClassMethodVisibilityBasedOnReflectionMethod($node, $parentReflectionMethod);
return $node;
@ -139,4 +147,72 @@ PHP
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');
});
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector\Fixture;
class SkipStaticCtor extends ParentWithPublicConstructor
{
protected function __construct()
{
}
public static function create()
{
return new self();
}
}
class ParentWithPublicConstructor
{
public function __construct()
{
}
}

View File

@ -0,0 +1,36 @@
<?php declare(strict_types=1);
namespace Rector\CodingStyle\Tests\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector;
use Iterator;
use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class SkipParentConstructOverrideInPHP72Test extends AbstractRectorTestCase
{
/**
* @requires PHP >= 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';
}
}