init MoveValueObjectsToValueObjectDirectoryRector (#2481)

init MoveValueObjectsToValueObjectDirectoryRector
This commit is contained in:
Tomas Votruba 2019-12-25 12:28:13 +01:00 committed by GitHub
commit a8b194244b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 193 additions and 0 deletions

View File

@ -84,6 +84,7 @@ parameters:
- 'packages/Php71/src/Rector/FuncCall/RemoveExtraParametersRector.php'
- 'packages/SOLID/src/Analyzer/ClassConstantFetchAnalyzer.php'
# tough logic
- 'packages/Autodiscovery/src/Analyzer/ClassAnalyzer.php'
- 'packages/CodingStyle/src/Imports/ImportSkipper.php'
- 'packages/PHPUnit/src/Rector/Class_/ArrayArgumentInTestToDataProviderRector.php'
- 'packages/BetterPhpDocParser/src/Ast/PhpDoc/*/*TagValueNode.php'

View File

@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
namespace Rector\Autodiscovery\Analyzer;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\ObjectType;
use Rector\NodeContainer\ParsedNodesByType;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\PhpParser\Node\Resolver\NameResolver;
final class ClassAnalyzer
{
/**
* @var bool[]
*/
private $valueObjectStatusByClassName = [];
/**
* @var NameResolver
*/
private $nameResolver;
/**
* @var ParsedNodesByType
*/
private $parsedNodesByType;
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;
public function __construct(
NameResolver $nameResolver,
ParsedNodesByType $parsedNodesByType,
NodeTypeResolver $nodeTypeResolver,
DocBlockManipulator $docBlockManipulator
) {
$this->nameResolver = $nameResolver;
$this->parsedNodesByType = $parsedNodesByType;
$this->nodeTypeResolver = $nodeTypeResolver;
$this->docBlockManipulator = $docBlockManipulator;
}
public function isValueObjectClass(Class_ $class): bool
{
if ($class->isAnonymous()) {
return false;
}
$className = $this->nameResolver->getName($class);
if (isset($this->valueObjectStatusByClassName[$className])) {
return $this->valueObjectStatusByClassName[$className];
}
$constructClassMethod = $class->getMethod('__construct');
if ($constructClassMethod === null) {
// 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;
}
// resolve constructor types
foreach ($constructClassMethod->params as $param) {
$paramType = $this->nodeTypeResolver->getObjectType($param);
if (! $paramType instanceof ObjectType) {
continue;
}
// awesome!
// is it services or value object?
$paramTypeClass = $this->parsedNodesByType->findClass($paramType->getClassName());
if ($paramTypeClass === null) {
// 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 hasAllPropertiesWithSerialize(Class_ $class)
{
foreach ($class->stmts as $stmt) {
if (! $stmt instanceof Property) {
continue;
}
if ($this->docBlockManipulator->hasTag($stmt, 'JMS\Serializer\Annotation\Type')) {
continue;
}
return false;
}
return true;
}
}

View File

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace Rector\Autodiscovery\Rector\FileSystem;
use PhpParser\Node\Stmt\Class_;
use Rector\Autodiscovery\Analyzer\ClassAnalyzer;
use Rector\Autodiscovery\FileMover\FileMover;
use Rector\FileSystemRector\Rector\AbstractFileSystemRector;
use Rector\RectorDefinition\RectorDefinition;
use Symplify\SmartFileSystem\SmartFileInfo;
/**
* Inspiration @see https://github.com/rectorphp/rector/pull/1865/files#diff-0d18e660cdb626958662641b491623f8
* @wip
*
* @sponsor Thanks https://spaceflow.io/ for sponsoring this rule - visit them on https://github.com/SpaceFlow-app
*/
final class MoveValueObjectsToValueObjectDirectoryRector extends AbstractFileSystemRector
{
/**
* @var FileMover
*/
private $fileMover;
/**
* @var ClassAnalyzer
*/
private $classAnalyzer;
public function __construct(FileMover $fileMover, ClassAnalyzer $classAnalyzer)
{
$this->fileMover = $fileMover;
$this->classAnalyzer = $classAnalyzer;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Move value object to ValueObject namespace/directory');
}
public function refactor(SmartFileInfo $smartFileInfo): void
{
$nodes = $this->parseFileInfoToNodes($smartFileInfo);
/** @var Class_|null $class */
$class = $this->betterNodeFinder->findFirstInstanceOf($nodes, Class_::class);
if ($class === null) {
return;
}
// get class
if (! $this->classAnalyzer->isValueObjectClass($class)) {
return;
}
$moved = $this->fileMover->createMovedNodesAndFilePath($smartFileInfo, $nodes, 'ValueObject');
// nothing to move
if ($moved === null) {
return;
}
[$nodes, $newFileDestination] = $moved;
$this->removeFile($smartFileInfo);
$this->printNewNodesToFilePath($nodes, $newFileDestination);
}
}