rector/rules/Autodiscovery/Analyzer/ValueObjectClassAnalyzer.php
Tomas Votruba b2412ad62a
Refactor doctrine/annotation from dynamic to own static-reflection parser (#5974)
* remove doctrine/annotations

* Refactor doctrine/annotation parser to static reflection with phpdoc-parser

* remove doctirne-annotation-parser-syncer

* remove annotation stubs

* use nodes

* almost there

* [ci-review] Rector Rectify

* skip temporary

* phpstan: remove fixed messages

Co-authored-by: kaizen-ci <info@kaizen-ci.org>
2021-04-04 11:01:11 +02:00

127 lines
3.5 KiB
PHP

<?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\ValueObject\MethodName;
use Rector\NodeCollector\NodeCollector\NodeRepository;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
final class ValueObjectClassAnalyzer
{
/**
* @var array<string, bool>
*/
private $valueObjectStatusByClassName = [];
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
/**
* @var PhpDocInfoFactory
*/
private $phpDocInfoFactory;
/**
* @var NodeRepository
*/
private $nodeRepository;
public function __construct(
NodeNameResolver $nodeNameResolver,
NodeTypeResolver $nodeTypeResolver,
PhpDocInfoFactory $phpDocInfoFactory,
NodeRepository $nodeRepository
) {
$this->nodeNameResolver = $nodeNameResolver;
$this->nodeTypeResolver = $nodeTypeResolver;
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->nodeRepository = $nodeRepository;
}
public function isValueObjectClass(Class_ $class): bool
{
if ($class->isAnonymous()) {
return false;
}
/** @var string $className */
$className = $this->nodeNameResolver->getName($class);
if (isset($this->valueObjectStatusByClassName[$className])) {
return $this->valueObjectStatusByClassName[$className];
}
$constructClassMethod = $class->getMethod(MethodName::CONSTRUCT);
if (! $constructClassMethod instanceof ClassMethod) {
return $this->analyseWithoutConstructor($class, $className);
}
// resolve constructor types
foreach ($constructClassMethod->params as $param) {
$paramType = $this->nodeTypeResolver->resolve($param);
if (! $paramType instanceof ObjectType) {
continue;
}
// awesome!
// is it services or value object?
$paramTypeClass = $this->nodeRepository->findClass($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 analyseWithoutConstructor(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;
}
}