From 1205416200088982bdd423c23aff9d259350ec26 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 15 Oct 2019 17:14:47 +0200 Subject: [PATCH] init MoveValueObjectsToValueObjectDirectoryRector --- ecs.yaml | 1 + .../src/Analyzer/ClassAnalyzer.php | 122 ++++++++++++++++++ ...lueObjectsToValueObjectDirectoryRector.php | 70 ++++++++++ 3 files changed, 193 insertions(+) create mode 100644 packages/Autodiscovery/src/Analyzer/ClassAnalyzer.php create mode 100644 packages/Autodiscovery/src/Rector/FileSystem/MoveValueObjectsToValueObjectDirectoryRector.php diff --git a/ecs.yaml b/ecs.yaml index 0c76de13fe1e..99045f459d2b 100644 --- a/ecs.yaml +++ b/ecs.yaml @@ -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' diff --git a/packages/Autodiscovery/src/Analyzer/ClassAnalyzer.php b/packages/Autodiscovery/src/Analyzer/ClassAnalyzer.php new file mode 100644 index 000000000000..bead4622085e --- /dev/null +++ b/packages/Autodiscovery/src/Analyzer/ClassAnalyzer.php @@ -0,0 +1,122 @@ +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; + } +} diff --git a/packages/Autodiscovery/src/Rector/FileSystem/MoveValueObjectsToValueObjectDirectoryRector.php b/packages/Autodiscovery/src/Rector/FileSystem/MoveValueObjectsToValueObjectDirectoryRector.php new file mode 100644 index 000000000000..4db99ea9ec18 --- /dev/null +++ b/packages/Autodiscovery/src/Rector/FileSystem/MoveValueObjectsToValueObjectDirectoryRector.php @@ -0,0 +1,70 @@ +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); + } +}