mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-03 18:00:50 +00:00
[DX] Remove MoveValueObjectsToValueObjectDirectoryRector, should be handled by PHPStorm refactoring and PHPStan rule checks (#1832)
This commit is contained in:
parent
cab8299093
commit
1ed8242e44
|
@ -1,4 +1,4 @@
|
|||
# 518 Rules Overview
|
||||
# 517 Rules Overview
|
||||
|
||||
<br>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
|||
|
||||
- [Arguments](#arguments) (4)
|
||||
|
||||
- [Autodiscovery](#autodiscovery) (4)
|
||||
- [Autodiscovery](#autodiscovery) (3)
|
||||
|
||||
- [CodeQuality](#codequality) (71)
|
||||
|
||||
|
@ -326,53 +326,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
<br>
|
||||
|
||||
### MoveValueObjectsToValueObjectDirectoryRector
|
||||
|
||||
Move value object to ValueObject namespace/directory
|
||||
|
||||
:wrench: **configure it!**
|
||||
|
||||
- class: [`Rector\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector`](../rules/Autodiscovery/Rector/Class_/MoveValueObjectsToValueObjectDirectoryRector.php)
|
||||
|
||||
```php
|
||||
use Rector\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(MoveValueObjectsToValueObjectDirectoryRector::class)
|
||||
->configure([
|
||||
MoveValueObjectsToValueObjectDirectoryRector::TYPES => ['ValueObjectInterfaceClassName'],
|
||||
MoveValueObjectsToValueObjectDirectoryRector::SUFFIXES => ['Search'],
|
||||
MoveValueObjectsToValueObjectDirectoryRector::ENABLE_VALUE_OBJECT_GUESSING => true,
|
||||
]);
|
||||
};
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```diff
|
||||
-// app/Exception/Name.php
|
||||
+// app/ValueObject/Name.php
|
||||
class Name
|
||||
{
|
||||
private $name;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## CodeQuality
|
||||
|
||||
### AbsolutizeRequireAndIncludePathRector
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\ValueObject;
|
||||
|
||||
final class MeSearch
|
||||
{
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\ValueObject;
|
||||
|
||||
class PrimitiveValueObject
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\ValueObject;
|
||||
|
||||
use Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\ObviousValueObjectInterface;
|
||||
final class SomeName implements \Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\ObviousValueObjectInterface
|
||||
{
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\FileSystemRector\ValueObject\AddedFileWithContent;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
final class MoveValueObjectsToValueObjectDirectoryRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fixtureFileInfo, ?AddedFileWithContent $expectedAddedFileWithContent): void
|
||||
{
|
||||
$this->doTestFileInfo($fixtureFileInfo);
|
||||
|
||||
if ($expectedAddedFileWithContent !== null) {
|
||||
$this->assertFileWasAdded($expectedAddedFileWithContent);
|
||||
} else {
|
||||
$this->assertFileWasNotChanged($this->originalTempFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<mixed>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$smartFileSystem = new SmartFileSystem();
|
||||
|
||||
yield [
|
||||
new SmartFileInfo(__DIR__ . '/Source/Repository/PrimitiveValueObject.php'),
|
||||
new AddedFileWithContent(
|
||||
$this->getFixtureTempDirectory() . '/ValueObject/PrimitiveValueObject.php',
|
||||
$smartFileSystem->readFile(__DIR__ . '/Expected/ValueObject/PrimitiveValueObject.php')
|
||||
),
|
||||
];
|
||||
|
||||
// type
|
||||
yield [
|
||||
new SmartFileInfo(__DIR__ . '/Source/Command/SomeName.php'),
|
||||
new AddedFileWithContent(
|
||||
$this->getFixtureTempDirectory() . '/ValueObject/SomeName.php',
|
||||
$smartFileSystem->readFile(__DIR__ . '/Expected/ValueObject/SomeName.php')
|
||||
),
|
||||
];
|
||||
|
||||
// suffix
|
||||
yield [
|
||||
new SmartFileInfo(__DIR__ . '/Source/Command/MeSearch.php'),
|
||||
new AddedFileWithContent(
|
||||
$this->getFixtureTempDirectory() . '/ValueObject/MeSearch.php',
|
||||
$smartFileSystem->readFile(__DIR__ . '/Expected/ValueObject/MeSearch.php')
|
||||
),
|
||||
];
|
||||
|
||||
// skip known service types
|
||||
yield [new SmartFileInfo(__DIR__ . '/Source/Utils/SomeSuffixedTest.php.inc'), null];
|
||||
}
|
||||
|
||||
public function provideConfigFilePath(): string
|
||||
{
|
||||
return __DIR__ . '/config/configured_rule.php';
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\Command;
|
||||
|
||||
final class MeSearch
|
||||
{
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\Command;
|
||||
|
||||
use Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\ObviousValueObjectInterface;
|
||||
|
||||
final class SomeName implements ObviousValueObjectInterface
|
||||
{
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source;
|
||||
|
||||
interface ObviousValueObjectInterface
|
||||
{
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\Repository;
|
||||
|
||||
class PrimitiveValueObject
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\Utils;
|
||||
|
||||
final class SomeSuffixedTest
|
||||
{
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector;
|
||||
use Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\Source\ObviousValueObjectInterface;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(MoveValueObjectsToValueObjectDirectoryRector::class)
|
||||
->configure([
|
||||
MoveValueObjectsToValueObjectDirectoryRector::TYPES => [ObviousValueObjectInterface::class],
|
||||
MoveValueObjectsToValueObjectDirectoryRector::SUFFIXES => ['Search'],
|
||||
MoveValueObjectsToValueObjectDirectoryRector::ENABLE_VALUE_OBJECT_GUESSING => true,
|
||||
]);
|
||||
};
|
|
@ -1,104 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Autodiscovery\Analyzer;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
|
||||
use Rector\Core\PhpParser\AstResolver;
|
||||
use Rector\Core\ValueObject\MethodName;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
final class ValueObjectClassAnalyzer
|
||||
{
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private array $valueObjectStatusByClassName = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly NodeTypeResolver $nodeTypeResolver,
|
||||
private readonly PhpDocInfoFactory $phpDocInfoFactory,
|
||||
private readonly AstResolver $astResolver,
|
||||
private readonly ClassAnalyzer $classAnalyzer,
|
||||
private readonly NodeNameResolver $nodeNameResolver
|
||||
) {
|
||||
}
|
||||
|
||||
public function isValueObjectClass(Class_ $class): bool
|
||||
{
|
||||
if ($this->classAnalyzer->isAnonymousClass($class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $className */
|
||||
$className = (string) $this->nodeNameResolver->getName($class);
|
||||
|
||||
if (isset($this->valueObjectStatusByClassName[$className])) {
|
||||
return $this->valueObjectStatusByClassName[$className];
|
||||
}
|
||||
|
||||
$constructClassMethod = $class->getMethod(MethodName::CONSTRUCT);
|
||||
|
||||
if (! $constructClassMethod instanceof ClassMethod) {
|
||||
return $this->hasExlusivelySerializeProperties($class, $className);
|
||||
}
|
||||
|
||||
// resolve constructor types
|
||||
foreach ($constructClassMethod->params as $param) {
|
||||
$paramType = $this->nodeTypeResolver->getType($param);
|
||||
if (! $paramType instanceof ObjectType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// awesome!
|
||||
// is it services or value object?
|
||||
$paramTypeClass = $this->astResolver->resolveClassFromName($paramType->getClassName());
|
||||
if (! $paramTypeClass instanceof Class_) {
|
||||
// not sure :/
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->isValueObjectClass($paramTypeClass)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't prove it's not a value object so far → fallback to true
|
||||
$this->valueObjectStatusByClassName[$className] = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function hasExlusivelySerializeProperties(Class_ $class, string $className): bool
|
||||
{
|
||||
// A. has all properties with serialize?
|
||||
if ($this->hasAllPropertiesWithSerialize($class)) {
|
||||
$this->valueObjectStatusByClassName[$className] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// probably not a value object
|
||||
$this->valueObjectStatusByClassName[$className] = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hasAllPropertiesWithSerialize(Class_ $class): bool
|
||||
{
|
||||
foreach ($class->getProperties() as $property) {
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
|
||||
if ($phpDocInfo->hasByAnnotationClass('JMS\Serializer\Annotation\Type')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Autodiscovery\Rector\Class_;
|
||||
|
||||
use Controller;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\Autodiscovery\Analyzer\ValueObjectClassAnalyzer;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\FileSystemRector\ValueObject\AddedFileWithNodes;
|
||||
use Rector\FileSystemRector\ValueObjectFactory\AddedFileWithNodesFactory;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* Inspiration @see https://github.com/rectorphp/rector/pull/1865/files#diff-0d18e660cdb626958662641b491623f8
|
||||
* @wip
|
||||
*
|
||||
* @see \Rector\Tests\Autodiscovery\Rector\Class_\MoveValueObjectsToValueObjectDirectoryRector\MoveValueObjectsToValueObjectDirectoryRectorTest
|
||||
*/
|
||||
final class MoveValueObjectsToValueObjectDirectoryRector extends AbstractRector implements ConfigurableRectorInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const TYPES = 'types';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SUFFIXES = 'suffixes';
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @var string
|
||||
*/
|
||||
public const ENABLE_VALUE_OBJECT_GUESSING = 'enable_value_object_guessing';
|
||||
|
||||
/**
|
||||
* @var string[]|class-string<Controller>[]
|
||||
*/
|
||||
private const COMMON_SERVICE_SUFFIXES = [
|
||||
'Repository', 'Command', 'Mapper', 'Controller', 'Presenter', 'Factory', 'Test', 'TestCase', 'Service',
|
||||
];
|
||||
|
||||
private bool $enableValueObjectGuessing = true;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $types = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $suffixes = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly AddedFileWithNodesFactory $addedFileWithNodesFactory,
|
||||
private readonly ValueObjectClassAnalyzer $valueObjectClassAnalyzer
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Move value object to ValueObject namespace/directory', [
|
||||
new ConfiguredCodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
// app/Exception/Name.php
|
||||
class Name
|
||||
{
|
||||
private $name;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
// app/ValueObject/Name.php
|
||||
class Name
|
||||
{
|
||||
private $name;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
[
|
||||
self::TYPES => ['ValueObjectInterfaceClassName'],
|
||||
self::SUFFIXES => ['Search'],
|
||||
self::ENABLE_VALUE_OBJECT_GUESSING => true,
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isValueObjectMatch($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$smartFileInfo = $this->file->getSmartFileInfo();
|
||||
|
||||
$addedFileWithNodes = $this->addedFileWithNodesFactory->createWithDesiredGroup(
|
||||
$smartFileInfo,
|
||||
$this->file,
|
||||
'ValueObject'
|
||||
);
|
||||
|
||||
if (! $addedFileWithNodes instanceof AddedFileWithNodes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->removedAndAddedFilesCollector->removeFile($smartFileInfo);
|
||||
$this->removedAndAddedFilesCollector->addAddedFile($addedFileWithNodes);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$types = $configuration[self::TYPES] ?? [];
|
||||
Assert::isArray($types);
|
||||
Assert::allString($types);
|
||||
$this->types = $types;
|
||||
|
||||
$suffixes = $configuration[self::SUFFIXES] ?? [];
|
||||
Assert::isArray($suffixes);
|
||||
Assert::allString($suffixes);
|
||||
$this->suffixes = $suffixes;
|
||||
|
||||
$enableValueObjectGuessing = $configuration[self::ENABLE_VALUE_OBJECT_GUESSING] ?? false;
|
||||
Assert::boolean($enableValueObjectGuessing);
|
||||
$this->enableValueObjectGuessing = $enableValueObjectGuessing;
|
||||
}
|
||||
|
||||
private function isValueObjectMatch(Class_ $class): bool
|
||||
{
|
||||
if ($this->isSuffixMatch($class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$className = $this->getName($class);
|
||||
if ($className === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classObjectType = new ObjectType($className);
|
||||
|
||||
foreach ($this->types as $type) {
|
||||
$desiredObjectType = new ObjectType($type);
|
||||
if ($desiredObjectType->isSuperTypeOf($classObjectType)->yes()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isKnownServiceType($className)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->enableValueObjectGuessing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->valueObjectClassAnalyzer->isValueObjectClass($class);
|
||||
}
|
||||
|
||||
private function isSuffixMatch(Class_ $class): bool
|
||||
{
|
||||
$className = $this->getName($class);
|
||||
if (! is_string($className)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->suffixes as $suffix) {
|
||||
if (\str_ends_with($className, $suffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isKnownServiceType(string $className): bool
|
||||
{
|
||||
foreach (self::COMMON_SERVICE_SUFFIXES as $commonServiceSuffix) {
|
||||
if (\str_ends_with($className, $commonServiceSuffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user