Merge pull request #543 from rectorphp/constant-visibility

[Rector] Add ChangeConstantVisibilityRector
This commit is contained in:
Tomáš Votruba 2018-07-31 13:36:18 +02:00 committed by GitHub
commit c9bfebaab7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 231 additions and 38 deletions

View File

@ -4,18 +4,24 @@ namespace Rector\NodeModifier;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use Rector\Exception\InvalidNodeTypeException;
final class VisibilityModifier
{
/**
* @var string[]
*/
private $allowedNodeTypes = [ClassMethod::class, Property::class, ClassConst::class];
/**
* This way "abstract", "static", "final" are kept
*
* @param ClassMethod|Property $node
* @param ClassMethod|Property|ClassConst $node
*/
public function removeOriginalVisibilityFromFlags($node): void
public function removeOriginalVisibilityFromFlags(Node $node): void
{
$this->ensureIsClassMethodOrProperty($node, __METHOD__);
@ -32,16 +38,38 @@ final class VisibilityModifier
}
}
/**
* @param ClassMethod|Property|ClassConst $node
*/
public function addVisibilityFlag(Node $node, string $visibility): void
{
$this->ensureIsClassMethodOrProperty($node, __METHOD__);
if ($visibility === 'public') {
$node->flags |= Class_::MODIFIER_PUBLIC;
}
if ($visibility === 'protected') {
$node->flags |= Class_::MODIFIER_PROTECTED;
}
if ($visibility === 'private') {
$node->flags |= Class_::MODIFIER_PRIVATE;
}
}
private function ensureIsClassMethodOrProperty(Node $node, string $location): void
{
if ($node instanceof ClassMethod || $node instanceof Property) {
return;
foreach ($this->allowedNodeTypes as $allowedNodeType) {
if (is_a($node, $allowedNodeType, true)) {
return;
}
}
throw new InvalidNodeTypeException(sprintf(
'"%s" only accepts "%s" types. "%s" given.',
$location,
implode('", "', [ClassMethod::class, Property::class]),
implode('", "', $this->allowedNodeTypes),
get_class($node)
));
}

View File

@ -0,0 +1,107 @@
<?php declare(strict_types=1);
namespace Rector\Rector\Visibility;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassConst;
use Rector\Node\Attribute;
use Rector\NodeModifier\VisibilityModifier;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
final class ChangeConstantVisibilityRector extends AbstractRector
{
/**
* @var string[] { class => [ method name => visibility ] }
*/
private $constantToVisibilityByClass = [];
/**
* @var VisibilityModifier
*/
private $visibilityModifier;
/**
* @param string[] $constantToVisibilityByClass
*/
public function __construct(array $constantToVisibilityByClass, VisibilityModifier $visibilityModifier)
{
$this->constantToVisibilityByClass = $constantToVisibilityByClass;
$this->visibilityModifier = $visibilityModifier;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Change visibility of constant from parent class.',
[new CodeSample(
<<<'CODE_SAMPLE'
class FrameworkClass
{
protected const SOME_CONSTANT = 1;
}
class MyClass extends FrameworkClass
{
public const SOME_CONSTANT = 1;
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class FrameworkClass
{
protected const SOME_CONSTANT = 1;
}
class MyClass extends FrameworkClass
{
protected const SOME_CONSTANT = 1;
}
CODE_SAMPLE
)]
);
}
public function isCandidate(Node $node): bool
{
if (! $node instanceof ClassConst) {
return false;
}
// doesn't have a parent class
if (! $node->hasAttribute(Attribute::PARENT_CLASS_NAME)) {
return false;
}
$nodeParentClassName = $node->getAttribute(Attribute::PARENT_CLASS_NAME);
if (! isset($this->constantToVisibilityByClass[$nodeParentClassName])) {
return false;
}
$constantName = $node->consts[0]->name->toString();
return isset($this->constantToVisibilityByClass[$nodeParentClassName][$constantName]);
}
/**
* @param ClassConst $classConstantNode
*/
public function refactor(Node $classConstantNode): ?Node
{
$this->visibilityModifier->removeOriginalVisibilityFromFlags($classConstantNode);
$newVisibility = $this->resolveNewVisibilityForNode($classConstantNode);
$this->visibilityModifier->addVisibilityFlag($classConstantNode, $newVisibility);
return $classConstantNode;
}
private function resolveNewVisibilityForNode(ClassConst $classConstantNode): string
{
$nodeParentClassName = $classConstantNode->getAttribute(Attribute::PARENT_CLASS_NAME);
$constantName = $classConstantNode->consts[0]->name->toString();
return $this->constantToVisibilityByClass[$nodeParentClassName][$constantName];
}
}

View File

@ -3,7 +3,6 @@
namespace Rector\Rector\Visibility;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Node\Attribute;
use Rector\NodeModifier\VisibilityModifier;
@ -98,25 +97,20 @@ CODE_SAMPLE
*/
public function refactor(Node $classMethodNode): ?Node
{
$nodeParentClassName = $classMethodNode->getAttribute(Attribute::PARENT_CLASS_NAME);
$methodName = $classMethodNode->name->toString();
$newVisibility = $this->methodToVisibilityByClass[$nodeParentClassName][$methodName];
$this->visibilityModifier->removeOriginalVisibilityFromFlags($classMethodNode);
if ($newVisibility === 'public') {
$classMethodNode->flags |= Class_::MODIFIER_PUBLIC;
}
$newVisibility = $this->resolveNewVisibilityForNode($classMethodNode);
if ($newVisibility === 'protected') {
$classMethodNode->flags |= Class_::MODIFIER_PROTECTED;
}
if ($newVisibility === 'private') {
$classMethodNode->flags |= Class_::MODIFIER_PRIVATE;
}
$this->visibilityModifier->addVisibilityFlag($classMethodNode, $newVisibility);
return $classMethodNode;
}
private function resolveNewVisibilityForNode(ClassMethod $classMethodNode): string
{
$methodName = $classMethodNode->name->toString();
$nodeParentClassName = $classMethodNode->getAttribute(Attribute::PARENT_CLASS_NAME);
return $this->methodToVisibilityByClass[$nodeParentClassName][$methodName];
}
}

View File

@ -3,7 +3,6 @@
namespace Rector\Rector\Visibility;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use Rector\Node\Attribute;
use Rector\NodeModifier\VisibilityModifier;
@ -92,25 +91,19 @@ CODE_SAMPLE
*/
public function refactor(Node $propertyNode): ?Node
{
$nodeParentClassName = $propertyNode->getAttribute(Attribute::PARENT_CLASS_NAME);
$propertyName = $propertyNode->props[0]->name->toString();
$newVisibility = $this->propertyToVisibilityByClass[$nodeParentClassName][$propertyName];
$this->visibilityModifier->removeOriginalVisibilityFromFlags($propertyNode);
if ($newVisibility === 'public') {
$propertyNode->flags |= Class_::MODIFIER_PUBLIC;
}
if ($newVisibility === 'protected') {
$propertyNode->flags |= Class_::MODIFIER_PROTECTED;
}
if ($newVisibility === 'private') {
$propertyNode->flags |= Class_::MODIFIER_PRIVATE;
}
$newVisibility = $this->resolveNewVisibilityForNode($propertyNode);
$this->visibilityModifier->addVisibilityFlag($propertyNode, $newVisibility);
return $propertyNode;
}
private function resolveNewVisibilityForNode(Property $propertyNode): string
{
$nodeParentClassName = $propertyNode->getAttribute(Attribute::PARENT_CLASS_NAME);
$propertyName = $propertyNode->props[0]->name->toString();
return $this->propertyToVisibilityByClass[$nodeParentClassName][$propertyName];
}
}

View File

@ -0,0 +1,30 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Visibility\ChangeConstantVisibilityRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
/**
* @covers \Rector\Rector\Visibility\ChangeConstantVisibilityRector
*/
final class ChangeConstantVisibilityRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideWrongToFixedFiles()
*/
public function test(string $wrong, string $fixed): void
{
$this->doTestFileMatchesExpectedContent($wrong, $fixed);
}
public function provideWrongToFixedFiles(): Iterator
{
yield [__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'];
}
protected function provideConfig(): string
{
return __DIR__ . '/config.yml';
}
}

View File

@ -0,0 +1,12 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Visibility\ChangePropertyVisibilityRector\Source;
use Rector\Tests\Rector\Visibility\ChangeConstantVisibilityRector\Source\ParentObject;
class ClassWithInvalidConstants extends ParentObject
{
public const TO_BE_PUBLIC_CONSTANT = 1;
protected const TO_BE_PROTECTED_CONSTANT = 2;
private const TO_BE_PRIVATE_CONSTANT = 3;
}

View File

@ -0,0 +1,10 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Visibility\ChangeConstantVisibilityRector\Source;
class ParentObject
{
public const TO_BE_PUBLIC_CONSTANT = 1;
protected const TO_BE_PROTECTED_CONSTANT = 2;
private const TO_BE_PRIVATE_CONSTANT = 3;
}

View File

@ -0,0 +1,12 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Visibility\ChangePropertyVisibilityRector\Source;
use Rector\Tests\Rector\Visibility\ChangeConstantVisibilityRector\Source\ParentObject;
class ClassWithInvalidConstants extends ParentObject
{
private const TO_BE_PUBLIC_CONSTANT = 1;
public const TO_BE_PROTECTED_CONSTANT = 2;
protected const TO_BE_PRIVATE_CONSTANT = 3;
}

View File

@ -0,0 +1,7 @@
services:
Rector\Rector\Visibility\ChangeConstantVisibilityRector:
$constantToVisibilityByClass:
'Rector\Tests\Rector\Visibility\ChangeConstantVisibilityRector\Source\ParentObject':
'TO_BE_PUBLIC_CONSTANT': 'public'
'TO_BE_PROTECTED_CONSTANT': 'protected'
'TO_BE_PRIVATE_CONSTANT': 'private'