[Naming] Make UnderscoreToCamelCasePropertyNameRector work with PropertyRenamer (#4283)

Co-authored-by: rector-bot <tomas@getrector.org>
This commit is contained in:
Igor 2020-09-28 20:29:37 +02:00 committed by GitHub
parent 19f83dd2b0
commit ccb086aab7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 889 additions and 328 deletions

View File

@ -22,7 +22,6 @@ use Rector\CodingStyle\Rector\Function_\CamelCaseFunctionNamingToUnderscoreRecto
use Rector\CodingStyle\Rector\If_\NullableCompareToNullRector;
use Rector\CodingStyle\Rector\Include_\FollowRequireByDirRector;
use Rector\CodingStyle\Rector\Plus\UseIncrementAssignRector;
use Rector\CodingStyle\Rector\PropertyProperty\UnderscoreToCamelCasePropertyNameRector;
use Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector;
use Rector\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector;
use Rector\CodingStyle\Rector\Switch_\BinarySwitchToIfElseRector;
@ -70,7 +69,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(CamelCaseFunctionNamingToUnderscoreRector::class);
$services->set(SplitGroupedUseImportsRector::class);
$services->set(UnderscoreToCamelCasePropertyNameRector::class);
$services->set(UnderscoreToCamelCaseVariableNameRector::class);
$services->set(RemoveDoubleUnderscoreInMethodNameRector::class);
};

View File

@ -10,6 +10,7 @@ use Rector\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector;
use Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector;
use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchMethodCallReturnTypeRector;
use Rector\Naming\Rector\Property\MakeBoolPropertyRespectIsHasWasMethodNamingRector;
use Rector\Naming\Rector\Property\UnderscoreToCamelCasePropertyNameRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -22,4 +23,5 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(MakeIsserClassMethodNameStartWithIsRector::class);
$services->set(RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class);
$services->set(MakeBoolPropertyRespectIsHasWasMethodNamingRector::class);
$services->set(UnderscoreToCamelCasePropertyNameRector::class);
};

View File

@ -10,6 +10,7 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\ClassLike;
@ -201,6 +202,15 @@ final class NodeNameResolver
return $this->isName($node->name, $name);
}
public function isLocalStaticPropertyFetchNamed(Node $node, string $name): bool
{
if (! $node instanceof StaticPropertyFetch) {
return false;
}
return $this->isName($node->name, $name);
}
/**
* @param MethodCall|StaticCall $node
*/

View File

@ -1,104 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Rector\PropertyProperty;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\PropertyProperty;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Core\Util\StaticRectorStrings;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* @see \Rector\CodingStyle\Tests\Rector\PropertyProperty\UnderscoreToCamelCasePropertyNameRector\UnderscoreToCamelCasePropertyNameRectorTest
*/
final class UnderscoreToCamelCasePropertyNameRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Change under_score names to camelCase', [
new CodeSample(
<<<'CODE_SAMPLE'
final class SomeClass
{
public $property_name;
public function run($a)
{
$this->property_name = 5;
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
final class SomeClass
{
public $propertyName;
public function run($a)
{
$this->propertyName = 5;
}
}
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [PropertyProperty::class, PropertyFetch::class, StaticPropertyFetch::class];
}
/**
* @param PropertyProperty|PropertyFetch|StaticPropertyFetch $node
*/
public function refactor(Node $node): ?Node
{
$nodeName = $this->getName($node);
if ($nodeName === null) {
return null;
}
/** @var string $class */
$class = $node->getAttribute(AttributeKey::CLASS_NAME);
// properties are accessed via magic, nothing we can do
if (method_exists($class, '__set') || method_exists($class, '__get')) {
return null;
}
if (! Strings::contains($nodeName, '_')) {
return null;
}
$camelCaseName = $this->createCamelName($nodeName, $node);
$node->name = new Identifier($camelCaseName);
return $node;
}
/**
* @param PropertyProperty|PropertyFetch|StaticPropertyFetch $node
*/
private function createCamelName(string $nodeName, Node $node): string
{
$camelCaseName = StaticRectorStrings::underscoreToCamelCase($nodeName);
if ($node instanceof StaticPropertyFetch || $node instanceof PropertyProperty) {
$camelCaseName = '$' . $camelCaseName;
}
return $camelCaseName;
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\ExpectedNameResolver;
use Nette\Utils\Strings;
use PhpParser\Node\Stmt\Property;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
abstract class AbstractExpectedNameResolver implements ExpectedNameResolverInterface
{
/**
* @var NodeTypeResolver
*/
protected $nodeTypeResolver;
/**
* @var PropertyNaming
*/
protected $propertyNaming;
/**
* @var NodeNameResolver
*/
protected $nodeNameResolver;
public function __construct(
NodeNameResolver $nodeNameResolver,
NodeTypeResolver $nodeTypeResolver,
PropertyNaming $propertyNaming
) {
$this->nodeNameResolver = $nodeNameResolver;
$this->nodeTypeResolver = $nodeTypeResolver;
$this->propertyNaming = $propertyNaming;
}
public function resolveIfNotYet(Property $property): ?string
{
$expectedName = $this->resolve($property);
if ($expectedName === null) {
return null;
}
/** @var string $propertyName */
$propertyName = $this->nodeNameResolver->getName($property);
if ($this->endsWith($propertyName, $expectedName)) {
return null;
}
if ($this->nodeNameResolver->isName($property, $expectedName)) {
return null;
}
return $expectedName;
}
/**
* Ends with ucname
* Starts with adjective, e.g. (Post $firstPost, Post $secondPost)
*/
protected function endsWith(string $currentName, string $expectedName): bool
{
$suffixNamePattern = '#\w+' . ucfirst($expectedName) . '#';
return (bool) Strings::match($currentName, $suffixNamePattern);
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\ExpectedNameResolver;
use PhpParser\Node\Stmt\Property;
final class BoolPropertyExpectedNameResolver extends AbstractExpectedNameResolver
{
public function resolve(Property $property): ?string
{
if ($this->nodeTypeResolver->isPropertyBoolean($property)) {
return $this->propertyNaming->getExpectedNameFromBooleanPropertyType($property);
}
return null;
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\ExpectedNameResolver;
use PhpParser\Node\Stmt\Property;
interface ExpectedNameResolverInterface
{
public function resolveIfNotYet(Property $property): ?string;
public function resolve(Property $property): ?string;
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\ExpectedNameResolver;
use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class MatchPropertyTypeExpectedNameResolver extends AbstractExpectedNameResolver
{
public function resolve(Property $property): ?string
{
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return null;
}
$expectedName = $this->propertyNaming->getExpectedNameFromType($phpDocInfo->getVarType());
if ($expectedName === null) {
return null;
}
return $expectedName->getName();
}
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\ExpectedNameResolver;
use PhpParser\Node\Stmt\Property;
use Rector\Core\Util\StaticRectorStrings;
final class UnderscoreCamelCaseExpectedNameResolver extends AbstractExpectedNameResolver
{
public function resolve(Property $property): string
{
$currentName = $this->nodeNameResolver->getName($property);
return StaticRectorStrings::underscoreToCamelCase($currentName);
}
}

View File

@ -22,7 +22,6 @@ use Ramsey\Uuid\UuidInterface;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Naming\Naming\ConflictingNameResolver;
use Rector\Naming\Naming\OverridenExistingNamesResolver;
use Rector\Naming\ValueObject\PropertyRename;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
@ -125,26 +124,6 @@ final class BreakingVariableRenameGuard
return $this->isUsedInIfAndOtherBranches($variable, $currentName);
}
public function shouldSkipProperty(PropertyRename $propertyRename): bool
{
if (! $propertyRename->getProperty()->isPrivate()) {
return true;
}
$conflictingPropertyNames = $this->conflictingNameResolver->resolveConflictingPropertyNames(
$propertyRename->getClassLike()
);
if (in_array($propertyRename->getExpectedName(), $conflictingPropertyNames, true)) {
return true;
}
if ($this->isRamseyUuidInterface($propertyRename->getProperty())) {
return true;
}
return $this->isDateTimeAtNamingConvention($propertyRename->getProperty());
}
public function shouldSkipParam(
string $currentName,
string $expectedName,
@ -266,6 +245,7 @@ final class BreakingVariableRenameGuard
}
/**
* @TODO Remove once ParamRenamer created
* @param Param|Property $node
*/
private function isRamseyUuidInterface(Node $node): bool
@ -274,6 +254,7 @@ final class BreakingVariableRenameGuard
}
/**
* @TODO Remove once ParamRenamer created
* @param Param|Property $node
*/
private function isDateTimeAtNamingConvention(Node $node): bool

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Guard;
use DateTimeInterface;
use Nette\Utils\Strings;
use PHPStan\Type\TypeWithClassName;
use Rector\Naming\ValueObject\RenameValueObjectInterface;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
final class DateTimeAtNamingConventionGuard implements GuardInterface
{
/**
* @var string
*/
private const AT_NAMING_REGEX = '#[\w+]At$#';
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
/**
* @var TypeUnwrapper
*/
private $typeUnwrapper;
public function __construct(NodeTypeResolver $nodeTypeResolver, TypeUnwrapper $typeUnwrapper)
{
$this->nodeTypeResolver = $nodeTypeResolver;
$this->typeUnwrapper = $typeUnwrapper;
}
public function check(RenameValueObjectInterface $renameValueObject): bool
{
return $this->isDateTimeAtNamingConvention($renameValueObject);
}
private function isDateTimeAtNamingConvention(RenameValueObjectInterface $renameValueObject): bool
{
$type = $this->nodeTypeResolver->resolve($renameValueObject->getNode());
$type = $this->typeUnwrapper->unwrapFirstObjectTypeFromUnionType($type);
if (! $type instanceof TypeWithClassName) {
return false;
}
if (! is_a($type->getClassName(), DateTimeInterface::class, true)) {
return false;
}
return (bool) Strings::match($renameValueObject->getCurrentName(), self::AT_NAMING_REGEX . '');
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Guard;
use Rector\Naming\ValueObject\RenameValueObjectInterface;
interface GuardInterface
{
public function check(RenameValueObjectInterface $renameValueObject): bool;
}

View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Guard;
use Rector\Naming\ValueObject\PropertyRename;
use Rector\Naming\ValueObject\RenameValueObjectInterface;
final class HasMagicGetSetGuard implements GuardInterface
{
/**
* @param PropertyRename $renameValueObject
*/
public function check(RenameValueObjectInterface $renameValueObject): bool
{
return method_exists($renameValueObject->getClassLikeName(), '__set') || method_exists(
$renameValueObject->getClassLikeName(),
'__get'
);
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Guard;
use Rector\Naming\ValueObject\PropertyRename;
use Rector\Naming\ValueObject\RenameValueObjectInterface;
final class NotPrivatePropertyGuard implements GuardInterface
{
/**
* @param PropertyRename $renameValueObject
*/
public function check(RenameValueObjectInterface $renameValueObject): bool
{
return ! $renameValueObject->getNode()->isPrivate();
}
}

View File

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Guard\PropertyConflictingNameGuard;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use Rector\Naming\ExpectedNameResolver\ExpectedNameResolverInterface;
use Rector\Naming\Guard\GuardInterface;
use Rector\Naming\PhpArray\ArrayFilter;
use Rector\Naming\ValueObject\RenameValueObjectInterface;
use Rector\NodeNameResolver\NodeNameResolver;
class AbstractPropertyConflictingNameGuard implements GuardInterface
{
/**
* @var ExpectedNameResolverInterface
*/
protected $expectedNameResolver;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var ArrayFilter
*/
private $arrayFilter;
public function __construct(NodeNameResolver $nodeNameResolver, ArrayFilter $arrayFilter)
{
$this->nodeNameResolver = $nodeNameResolver;
$this->arrayFilter = $arrayFilter;
}
public function check(RenameValueObjectInterface $renameValueObject): bool
{
$conflictingPropertyNames = $this->resolve($renameValueObject->getClassLike());
return in_array($renameValueObject->getExpectedName(), $conflictingPropertyNames, true);
}
/**
* @param ClassLike $node
* @return string[]
*/
public function resolve(Node $node): array
{
$expectedNames = [];
foreach ($node->getProperties() as $property) {
$expectedName = $this->expectedNameResolver->resolve($property);
if ($expectedName === null) {
/** @var string $expectedName */
$expectedName = $this->nodeNameResolver->getName($property);
}
$expectedNames[] = $expectedName;
}
return $this->arrayFilter->filterWithAtLeastTwoOccurences($expectedNames);
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Guard\PropertyConflictingNameGuard;
use Rector\Naming\ExpectedNameResolver\BoolPropertyExpectedNameResolver;
final class BoolPropertyConflictingNameGuard extends AbstractPropertyConflictingNameGuard
{
/**
* @required
*/
public function autowireBoolPropertyConflictingNameGuard(
BoolPropertyExpectedNameResolver $boolPropertyExpectedNameResolver
): void {
$this->expectedNameResolver = $boolPropertyExpectedNameResolver;
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Guard\PropertyConflictingNameGuard;
use Rector\Naming\ExpectedNameResolver\MatchPropertyTypeExpectedNameResolver;
final class MatchPropertyTypeConflictingNameGuard extends AbstractPropertyConflictingNameGuard
{
/**
* @required
*/
public function autowireMatchPropertyTypePropertyConflictingNameGuard(
MatchPropertyTypeExpectedNameResolver $matchPropertyTypeExpectedNameResolver
): void {
$this->expectedNameResolver = $matchPropertyTypeExpectedNameResolver;
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Guard\PropertyConflictingNameGuard;
use Rector\Naming\ExpectedNameResolver\UnderscoreCamelCaseExpectedNameResolver;
final class UnderscoreCamelCaseConflictingNameGuard extends AbstractPropertyConflictingNameGuard
{
/**
* @required
*/
public function autowireUnderscoreCamelCasePropertyConflictingNameGuard(
UnderscoreCamelCaseExpectedNameResolver $underscoreCamelCaseExpectedNameResolver
): void {
$this->expectedNameResolver = $underscoreCamelCaseExpectedNameResolver;
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Guard;
use Ramsey\Uuid\UuidInterface;
use Rector\Naming\ValueObject\RenameValueObjectInterface;
use Rector\NodeTypeResolver\NodeTypeResolver;
final class RamseyUuidInterfaceGuard implements GuardInterface
{
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
public function __construct(NodeTypeResolver $nodeTypeResolver)
{
$this->nodeTypeResolver = $nodeTypeResolver;
}
public function check(RenameValueObjectInterface $renameValueObject): bool
{
return $this->nodeTypeResolver->isObjectType($renameValueObject->getNode(), UuidInterface::class);
}
}

View File

@ -7,7 +7,6 @@ namespace Rector\Naming\Naming;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
@ -53,25 +52,6 @@ final class ConflictingNameResolver
$this->arrayFilter = $arrayFilter;
}
/**
* @return string[]
*/
public function resolveConflictingPropertyNames(ClassLike $classLike): array
{
$expectedNames = [];
foreach ($classLike->getProperties() as $property) {
$expectedName = $this->expectedNameResolver->resolveForProperty($property);
if ($expectedName === null) {
/** @var string $expectedName */
$expectedName = $this->nodeNameResolver->getName($property);
}
$expectedNames[] = $expectedName;
}
return $this->arrayFilter->filterWithAtLeastTwoOccurences($expectedNames);
}
/**
* @return string[]
*/

View File

@ -15,12 +15,10 @@ use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
@ -61,26 +59,6 @@ final class ExpectedNameResolver
$this->nodeTypeResolver = $nodeTypeResolver;
}
public function resolveForPropertyIfNotYet(Property $property): ?string
{
$expectedName = $this->resolveForProperty($property);
if ($expectedName === null) {
return null;
}
/** @var string $propertyName */
$propertyName = $this->nodeNameResolver->getName($property);
if ($this->endsWith($propertyName, $expectedName)) {
return null;
}
if ($this->nodeNameResolver->isName($property, $expectedName)) {
return null;
}
return $expectedName;
}
public function resolveForParamIfNotYet(Param $param): ?string
{
$expectedName = $this->resolveForParam($param);
@ -117,26 +95,6 @@ final class ExpectedNameResolver
return $expectedName->getName();
}
public function resolveForProperty(Property $property): ?string
{
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return null;
}
if ($this->nodeTypeResolver->isPropertyBoolean($property)) {
return $this->propertyNaming->getExpectedNameFromBooleanPropertyType($property);
}
$expectedName = $this->propertyNaming->getExpectedNameFromType($phpDocInfo->getVarType());
if ($expectedName === null) {
return null;
}
return $expectedName->getName();
}
public function resolveForAssignNonNew(Assign $assign): ?string
{
if ($assign->expr instanceof New_) {

View File

@ -1,82 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Naming;
use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\VarLikeIdentifier;
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
use Rector\Naming\Guard\BreakingVariableRenameGuard;
use Rector\Naming\ValueObject\PropertyRename;
use Rector\NodeNameResolver\NodeNameResolver;
final class PropertyRenamer
{
/**
* @var CallableNodeTraverser
*/
private $callableNodeTraverser;
/**
* @var BreakingVariableRenameGuard
*/
private $breakingVariableRenameGuard;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
public function __construct(
CallableNodeTraverser $callableNodeTraverser,
BreakingVariableRenameGuard $breakingVariableRenameGuard,
NodeNameResolver $nodeNameResolver
) {
$this->callableNodeTraverser = $callableNodeTraverser;
$this->breakingVariableRenameGuard = $breakingVariableRenameGuard;
$this->nodeNameResolver = $nodeNameResolver;
}
public function rename(PropertyRename $propertyRename): ?Property
{
if ($this->breakingVariableRenameGuard->shouldSkipProperty($propertyRename)) {
return null;
}
if ($this->areNamesDifferent($propertyRename)) {
return null;
}
$onlyPropertyProperty = $propertyRename->getPropertyProperty();
$onlyPropertyProperty->name = new VarLikeIdentifier($propertyRename->getExpectedName());
$this->renamePropertyFetchesInClass($propertyRename);
return $propertyRename->getProperty();
}
private function areNamesDifferent(PropertyRename $propertyRename): bool
{
return $propertyRename->getCurrentName() === $propertyRename->getExpectedName();
}
private function renamePropertyFetchesInClass(PropertyRename $propertyRename): void
{
// 1. replace property fetch rename in whole class
$this->callableNodeTraverser->traverseNodesWithCallable(
[$propertyRename->getClassLike()],
function (Node $node) use ($propertyRename): ?PropertyFetch {
if (! $this->nodeNameResolver->isLocalPropertyFetchNamed($node, $propertyRename->getCurrentName())) {
return null;
}
/** @var PropertyFetch $node */
$node->name = new Identifier($propertyRename->getExpectedName());
return $node;
}
);
}
}

View File

@ -0,0 +1,140 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\PropertyRenamer;
use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\VarLikeIdentifier;
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
use Rector\Naming\Guard\DateTimeAtNamingConventionGuard;
use Rector\Naming\Guard\GuardInterface;
use Rector\Naming\Guard\HasMagicGetSetGuard;
use Rector\Naming\Guard\NotPrivatePropertyGuard;
use Rector\Naming\Guard\RamseyUuidInterfaceGuard;
use Rector\Naming\RenameGuard\PropertyRenameGuard;
use Rector\Naming\RenameGuard\RenameGuardInterface;
use Rector\Naming\ValueObject\PropertyRename;
use Rector\NodeNameResolver\NodeNameResolver;
abstract class AbstractPropertyRenamer
{
/**
* @var RenameGuardInterface
*/
protected $propertyRenameGuard;
/**
* @var GuardInterface
*/
protected $conflictingPropertyNameGuard;
/**
* @var CallableNodeTraverser
*/
private $callableNodeTraverser;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var NotPrivatePropertyGuard
*/
private $notPrivatePropertyGuard;
/**
* @var RamseyUuidInterfaceGuard
*/
private $ramseyUuidInterfaceGuard;
/**
* @var DateTimeAtNamingConventionGuard
*/
private $dateTimeAtNamingConventionGuard;
/**
* @var HasMagicGetSetGuard
*/
private $hasMagicGetSetGuard;
/**
* @required
*/
public function autowireAbstractPropertyRenamer(
CallableNodeTraverser $callableNodeTraverser,
NodeNameResolver $nodeNameResolver,
NotPrivatePropertyGuard $notPrivatePropertyGuard,
RamseyUuidInterfaceGuard $ramseyUuidInterfaceGuard,
DateTimeAtNamingConventionGuard $dateTimeAtNamingConventionGuard,
PropertyRenameGuard $propertyRenameGuard,
HasMagicGetSetGuard $hasMagicGetSetGuard
): void {
$this->callableNodeTraverser = $callableNodeTraverser;
$this->nodeNameResolver = $nodeNameResolver;
$this->notPrivatePropertyGuard = $notPrivatePropertyGuard;
$this->ramseyUuidInterfaceGuard = $ramseyUuidInterfaceGuard;
$this->dateTimeAtNamingConventionGuard = $dateTimeAtNamingConventionGuard;
$this->propertyRenameGuard = $propertyRenameGuard;
$this->hasMagicGetSetGuard = $hasMagicGetSetGuard;
}
public function rename(PropertyRename $propertyRename): ?Property
{
if ($this->areNamesDifferent($propertyRename)) {
return null;
}
if ($this->propertyRenameGuard->shouldSkip($propertyRename, [
$this->notPrivatePropertyGuard,
$this->conflictingPropertyNameGuard,
$this->ramseyUuidInterfaceGuard,
$this->dateTimeAtNamingConventionGuard,
$this->hasMagicGetSetGuard,
])) {
return null;
}
$onlyPropertyProperty = $propertyRename->getPropertyProperty();
$onlyPropertyProperty->name = new VarLikeIdentifier($propertyRename->getExpectedName());
$this->renamePropertyFetchesInClass($propertyRename);
return $propertyRename->getNode();
}
private function areNamesDifferent(PropertyRename $propertyRename): bool
{
return $propertyRename->getCurrentName() === $propertyRename->getExpectedName();
}
private function renamePropertyFetchesInClass(PropertyRename $propertyRename): void
{
// 1. replace property fetch rename in whole class
$this->callableNodeTraverser->traverseNodesWithCallable(
[$propertyRename->getClassLike()],
function (Node $node) use ($propertyRename): ?Node {
if ($this->nodeNameResolver->isLocalPropertyFetchNamed($node, $propertyRename->getCurrentName())) {
/** @var PropertyFetch $node */
$node->name = new Identifier($propertyRename->getExpectedName());
return $node;
}
if ($this->nodeNameResolver->isLocalStaticPropertyFetchNamed(
$node,
$propertyRename->getCurrentName()
)) {
/** @var StaticPropertyFetch $node */
$node->name = new VarLikeIdentifier($propertyRename->getExpectedName());
return $node;
}
return null;
}
);
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\PropertyRenamer;
use Rector\Naming\Guard\PropertyConflictingNameGuard\BoolPropertyConflictingNameGuard;
final class BoolPropertyRenamer extends AbstractPropertyRenamer
{
public function __construct(BoolPropertyConflictingNameGuard $boolPropertyConflictingNameGuard)
{
$this->conflictingPropertyNameGuard = $boolPropertyConflictingNameGuard;
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\PropertyRenamer;
use Rector\Naming\Guard\PropertyConflictingNameGuard\MatchPropertyTypeConflictingNameGuard;
final class MatchTypePropertyRenamer extends AbstractPropertyRenamer
{
public function __construct(MatchPropertyTypeConflictingNameGuard $matchPropertyTypeConflictingNameGuard)
{
$this->conflictingPropertyNameGuard = $matchPropertyTypeConflictingNameGuard;
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\PropertyRenamer;
use Rector\Naming\Guard\PropertyConflictingNameGuard\UnderscoreCamelCaseConflictingNameGuard;
final class UnderscoreCamelCasePropertyRenamer extends AbstractPropertyRenamer
{
public function __construct(UnderscoreCamelCaseConflictingNameGuard $underscoreCamelCaseConflictingNameGuard)
{
$this->conflictingPropertyNameGuard = $underscoreCamelCaseConflictingNameGuard;
}
}

View File

@ -11,7 +11,8 @@ use PhpParser\Node\Stmt\Interface_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Naming\PropertyRenamer;
use Rector\Naming\ExpectedNameResolver\MatchPropertyTypeExpectedNameResolver;
use Rector\Naming\PropertyRenamer\MatchTypePropertyRenamer;
use Rector\Naming\ValueObjectFactory\PropertyRenameFactory;
/**
@ -24,20 +25,29 @@ final class RenamePropertyToMatchTypeRector extends AbstractRector
*/
private $hasChanged = false;
/**
* @var PropertyRenamer
*/
private $propertyRenamer;
/**
* @var PropertyRenameFactory
*/
private $propertyRenameFactory;
public function __construct(PropertyRenamer $propertyRenamer, PropertyRenameFactory $propertyRenameFactory)
{
$this->propertyRenamer = $propertyRenamer;
/**
* @var MatchTypePropertyRenamer
*/
private $matchTypePropertyRenamer;
/**
* @var MatchPropertyTypeExpectedNameResolver
*/
private $matchPropertyTypeExpectedNameResolver;
public function __construct(
MatchTypePropertyRenamer $matchTypePropertyRenamer,
PropertyRenameFactory $propertyRenameFactory,
MatchPropertyTypeExpectedNameResolver $matchPropertyTypeExpectedNameResolver
) {
$this->propertyRenameFactory = $propertyRenameFactory;
$this->matchTypePropertyRenamer = $matchTypePropertyRenamer;
$this->matchPropertyTypeExpectedNameResolver = $matchPropertyTypeExpectedNameResolver;
}
public function getDefinition(): RectorDefinition
@ -102,12 +112,15 @@ CODE_SAMPLE
private function refactorClassProperties(ClassLike $classLike): void
{
foreach ($classLike->getProperties() as $property) {
$propertyRename = $this->propertyRenameFactory->create($property);
$propertyRename = $this->propertyRenameFactory->create(
$property,
$this->matchPropertyTypeExpectedNameResolver
);
if ($propertyRename === null) {
continue;
}
if ($this->propertyRenamer->rename($propertyRename) !== null) {
if ($this->matchTypePropertyRenamer->rename($propertyRename) !== null) {
$this->hasChanged = true;
}
}

View File

@ -9,7 +9,8 @@ use PhpParser\Node\Stmt\Property;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Naming\PropertyRenamer;
use Rector\Naming\ExpectedNameResolver\BoolPropertyExpectedNameResolver;
use Rector\Naming\PropertyRenamer\BoolPropertyRenamer;
use Rector\Naming\ValueObjectFactory\PropertyRenameFactory;
/**
@ -18,20 +19,29 @@ use Rector\Naming\ValueObjectFactory\PropertyRenameFactory;
*/
final class MakeBoolPropertyRespectIsHasWasMethodNamingRector extends AbstractRector
{
/**
* @var PropertyRenamer
*/
private $propertyRenamer;
/**
* @var PropertyRenameFactory
*/
private $propertyRenameFactory;
public function __construct(PropertyRenamer $propertyRenamer, PropertyRenameFactory $propertyRenameFactory)
{
$this->propertyRenamer = $propertyRenamer;
/**
* @var BoolPropertyRenamer
*/
private $boolPropertyRenamer;
/**
* @var BoolPropertyExpectedNameResolver
*/
private $boolPropertyExpectedNameResolver;
public function __construct(
BoolPropertyRenamer $boolPropertyRenamer,
PropertyRenameFactory $propertyRenameFactory,
BoolPropertyExpectedNameResolver $boolPropertyExpectedNameResolver
) {
$this->propertyRenameFactory = $propertyRenameFactory;
$this->boolPropertyRenamer = $boolPropertyRenamer;
$this->boolPropertyExpectedNameResolver = $boolPropertyExpectedNameResolver;
}
public function getDefinition(): RectorDefinition
@ -85,12 +95,13 @@ CODE_SAMPLE
return null;
}
$propertyRename = $this->propertyRenameFactory->create($node);
$propertyRename = $this->propertyRenameFactory->create($node, $this->boolPropertyExpectedNameResolver);
if ($propertyRename === null) {
return null;
}
if ($this->propertyRenamer->rename($propertyRename) === null) {
// dd($propertyRename->getClassLike());
if ($this->boolPropertyRenamer->rename($propertyRename) === null) {
return null;
}

View File

@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\Rector\Property;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt\Property;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Naming\ExpectedNameResolver\UnderscoreCamelCaseExpectedNameResolver;
use Rector\Naming\PropertyRenamer\UnderscoreCamelCasePropertyRenamer;
use Rector\Naming\ValueObjectFactory\PropertyRenameFactory;
/**
* @see \Rector\Naming\Tests\Rector\Property\UnderscoreToCamelCasePropertyNameRector\UnderscoreToCamelCasePropertyNameRectorTest
*/
final class UnderscoreToCamelCasePropertyNameRector extends AbstractRector
{
/**
* @var PropertyRenameFactory
*/
private $propertyRenameFactory;
/**
* @var UnderscoreCamelCasePropertyRenamer
*/
private $underscoreCamelCasePropertyRenamer;
/**
* @var UnderscoreCamelCaseExpectedNameResolver
*/
private $underscoreCamelCaseExpectedNameResolver;
public function __construct(
UnderscoreCamelCasePropertyRenamer $underscoreCamelCasePropertyRenamer,
PropertyRenameFactory $propertyRenameFactory,
UnderscoreCamelCaseExpectedNameResolver $underscoreCamelCaseExpectedNameResolver
) {
$this->underscoreCamelCasePropertyRenamer = $underscoreCamelCasePropertyRenamer;
$this->propertyRenameFactory = $propertyRenameFactory;
$this->underscoreCamelCaseExpectedNameResolver = $underscoreCamelCaseExpectedNameResolver;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Change under_score names to camelCase', [
new CodeSample(
<<<'CODE_SAMPLE'
final class SomeClass
{
public $property_name;
public function run($a)
{
$this->property_name = 5;
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
final class SomeClass
{
public $propertyName;
public function run($a)
{
$this->propertyName = 5;
}
}
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Property::class];
}
/**
* @param Property $node
*/
public function refactor(Node $node): ?Node
{
$nodeName = $this->getName($node);
if ($nodeName === null) {
return null;
}
if (! Strings::contains($nodeName, '_')) {
return null;
}
$propertyRename = $this->propertyRenameFactory->create($node, $this->underscoreCamelCaseExpectedNameResolver);
if ($propertyRename === null) {
return null;
}
if ($this->underscoreCamelCasePropertyRenamer->rename($propertyRename) === null) {
return null;
}
return $node;
}
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\RenameGuard;
use Rector\Naming\Guard\GuardInterface;
use Rector\Naming\ValueObject\RenameValueObjectInterface;
final class PropertyRenameGuard implements RenameGuardInterface
{
/**
* @param GuardInterface[] $guards
*/
public function shouldSkip(RenameValueObjectInterface $renameValueObject, array $guards): bool
{
foreach ($guards as $guard) {
if ($guard->check($renameValueObject)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\RenameGuard;
use Rector\Naming\Guard\GuardInterface;
use Rector\Naming\ValueObject\RenameValueObjectInterface;
interface RenameGuardInterface
{
/**
* @param GuardInterface[] $guards
*/
public function shouldSkip(RenameValueObjectInterface $renameValueObject, array $guards): bool;
}

View File

@ -4,11 +4,12 @@ declare(strict_types=1);
namespace Rector\Naming\ValueObject;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\PropertyProperty;
final class PropertyRename
final class PropertyRename implements RenameValueObjectInterface
{
/**
* @var string
@ -20,6 +21,11 @@ final class PropertyRename
*/
private $currentName;
/**
* @var string
*/
private $classLikeName;
/**
* @var Property
*/
@ -36,20 +42,20 @@ final class PropertyRename
private $propertyProperty;
public function __construct(
Property $property,
string $expectedName,
string $currentName,
ClassLike $classLike,
PropertyProperty $propertyProperty
Property $property, string $expectedName, string $currentName, ClassLike $classLike, string $classLikeName, PropertyProperty $propertyProperty
) {
$this->property = $property;
$this->expectedName = $expectedName;
$this->currentName = $currentName;
$this->classLike = $classLike;
$this->classLikeName = $classLikeName;
$this->propertyProperty = $propertyProperty;
}
public function getProperty(): Property
/**
* @return Property
*/
public function getNode(): Node
{
return $this->property;
}
@ -69,6 +75,11 @@ final class PropertyRename
return $this->classLike;
}
public function getClassLikeName(): string
{
return $this->classLikeName;
}
public function getPropertyProperty(): PropertyProperty
{
return $this->propertyProperty;

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\ValueObject;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
interface RenameValueObjectInterface
{
public function getNode(): Node;
public function getCurrentName(): string;
public function getExpectedName(): string;
public function getClassLike(): ClassLike;
}

View File

@ -6,7 +6,7 @@ namespace Rector\Naming\ValueObjectFactory;
use PhpParser\Node\Stmt\Property;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Naming\Naming\ExpectedNameResolver;
use Rector\Naming\ExpectedNameResolver\ExpectedNameResolverInterface;
use Rector\Naming\ValueObject\PropertyRename;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -16,29 +16,23 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
*/
final class PropertyRenameFactory
{
/**
* @var ExpectedNameResolver
*/
private $expectedNameResolver;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
public function __construct(ExpectedNameResolver $expectedNameResolver, NodeNameResolver $nodeNameResolver)
public function __construct(NodeNameResolver $nodeNameResolver)
{
$this->expectedNameResolver = $expectedNameResolver;
$this->nodeNameResolver = $nodeNameResolver;
}
public function create(Property $property): ?PropertyRename
public function create(Property $property, ExpectedNameResolverInterface $expectedNameResolver): ?PropertyRename
{
if (count($property->props) !== 1) {
return null;
}
$expectedName = $this->expectedNameResolver->resolveForPropertyIfNotYet($property);
$expectedName = $expectedNameResolver->resolveIfNotYet($property);
if ($expectedName === null) {
return null;
}
@ -47,9 +41,21 @@ final class PropertyRenameFactory
$propertyClassLike = $property->getAttribute(AttributeKey::CLASS_NODE);
if ($propertyClassLike === null) {
throw new ShouldNotHappenException("There shouldn't be a property without Class Node");
throw new ShouldNotHappenException("There shouldn't be a property without AttributeKey::CLASS_NODE");
}
return new PropertyRename($property, $expectedName, $currentName, $propertyClassLike, $property->props[0]);
$propertyClassLikeName = $property->getAttribute(AttributeKey::CLASS_NAME);
if ($propertyClassLikeName === null) {
throw new ShouldNotHappenException("There shouldn't be a property without AttributeKey::CLASS_NAME");
}
return new PropertyRename(
$property,
$expectedName,
$currentName,
$propertyClassLike,
$propertyClassLikeName,
$property->props[0]
);
}
}

View File

@ -1,16 +1,16 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Property\UnderscoreToCamelCasePropertyNameRector\Fixture;
namespace Rector\Naming\Tests\Rector\Property\UnderscoreToCamelCasePropertyNameRector\Fixture;
final class Property
{
public $some_property;
private $some_property;
public static $some_static_property;
private static $some_static_property;
public $_first_and_multiple_underscore_property;
private $_first_and_multiple_underscore_property;
public $_underscore;
private $_underscore;
public function run()
{
@ -26,17 +26,17 @@ final class Property
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Property\UnderscoreToCamelCasePropertyNameRector\Fixture;
namespace Rector\Naming\Tests\Rector\Property\UnderscoreToCamelCasePropertyNameRector\Fixture;
final class Property
{
public $someProperty;
private $someProperty;
public static $someStaticProperty;
private static $someStaticProperty;
public $firstAndMultipleUnderscoreProperty;
private $firstAndMultipleUnderscoreProperty;
public $underscore;
private $underscore;
public function run()
{

View File

@ -1,9 +1,11 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\PropertyProperty\UnderscoreToCamelCasePropertyNameRector\Fixture;
namespace Rector\Naming\Tests\Rector\Property\UnderscoreToCamelCasePropertyNameRector\Fixture;
class SkipMagicGet
{
private $underscore_value;
public function get()
{
$this->underscore_value = 5;

View File

@ -1,9 +1,11 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\PropertyProperty\UnderscoreToCamelCasePropertyNameRector\Fixture;
namespace Rector\Naming\Tests\Rector\Property\UnderscoreToCamelCasePropertyNameRector\Fixture;
class SkipMagicSet
{
private $underscore_value;
public function set()
{
$this->underscore_value = 5;

View File

@ -2,11 +2,11 @@
declare(strict_types=1);
namespace Rector\CodingStyle\Tests\Rector\PropertyProperty\UnderscoreToCamelCasePropertyNameRector;
namespace Rector\Naming\Tests\Rector\Property\UnderscoreToCamelCasePropertyNameRector;
use Iterator;
use Rector\CodingStyle\Rector\PropertyProperty\UnderscoreToCamelCasePropertyNameRector;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Naming\Rector\Property\UnderscoreToCamelCasePropertyNameRector;
use Symplify\SmartFileSystem\SmartFileInfo;
final class UnderscoreToCamelCasePropertyNameRectorTest extends AbstractRectorTestCase

View File

@ -10,6 +10,7 @@ use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\HttpKernel\RectorKernel;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\FileSystemRector\Parser\FileInfoParser;
use Rector\Naming\ExpectedNameResolver\MatchPropertyTypeExpectedNameResolver;
use Rector\Naming\ValueObject\PropertyRename;
use Rector\Naming\ValueObjectFactory\PropertyRenameFactory;
use Symplify\PackageBuilder\Tests\AbstractKernelTestCase;
@ -32,11 +33,20 @@ final class PropertyRenameFactoryTest extends AbstractKernelTestCase
*/
private $betterNodeFinder;
/**
* @var MatchPropertyTypeExpectedNameResolver
*/
private $matchPropertyTypeExpectedNameResolver;
protected function setUp(): void
{
$this->bootKernel(RectorKernel::class);
$this->propertyRenameFactory = self::$container->get(PropertyRenameFactory::class);
$this->matchPropertyTypeExpectedNameResolver = self::$container->get(
MatchPropertyTypeExpectedNameResolver::class
);
$this->fileInfoParser = self::$container->get(FileInfoParser::class);
$this->betterNodeFinder = self::$container->get(BetterNodeFinder::class);
}
@ -48,11 +58,14 @@ final class PropertyRenameFactoryTest extends AbstractKernelTestCase
{
$property = $this->getPropertyFromFileInfo($fileInfoWithProperty);
$actualPropertyRename = $this->propertyRenameFactory->create($property);
$actualPropertyRename = $this->propertyRenameFactory->create(
$property,
$this->matchPropertyTypeExpectedNameResolver
);
$this->assertNotNull($actualPropertyRename);
/** @var PropertyRename $actualPropertyRename */
$this->assertSame($property, $actualPropertyRename->getProperty());
$this->assertSame($property, $actualPropertyRename->getNode());
$this->assertSame($expectedName, $actualPropertyRename->getExpectedName());
$this->assertSame($currentName, $actualPropertyRename->getCurrentName());
}