rector/rules/RemovingStatic/UniqueObjectFactoryFactory.php

168 lines
7.2 KiB
PHP
Raw Normal View History

<?php
declare (strict_types=1);
namespace Rector\RemovingStatic;
use RectorPrefix20210510\Nette\Utils\Strings;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Return_;
2019-09-04 14:10:29 +02:00
use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\NodeFactory;
2020-08-02 15:17:33 +02:00
use Rector\Core\ValueObject\MethodName;
2020-07-01 23:41:49 +02:00
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\StaticTypeMapper\StaticTypeMapper;
use RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\ClassBuilder;
use RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder;
use RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\ParamBuilder;
final class UniqueObjectFactoryFactory
{
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var PropertyNaming
*/
private $propertyNaming;
2019-09-04 14:10:29 +02:00
/**
* @var StaticTypeMapper
*/
private $staticTypeMapper;
/**
* @var NodeFactory
*/
private $nodeFactory;
/**
* @var PhpDocTypeChanger
*/
private $phpDocTypeChanger;
/**
* @var PhpDocInfoFactory
*/
private $phpDocInfoFactory;
public function __construct(\Rector\Core\PhpParser\Node\NodeFactory $nodeFactory, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \Rector\Naming\Naming\PropertyNaming $propertyNaming, \Rector\StaticTypeMapper\StaticTypeMapper $staticTypeMapper, \Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger $phpDocTypeChanger, \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory $phpDocInfoFactory)
{
$this->nodeNameResolver = $nodeNameResolver;
$this->propertyNaming = $propertyNaming;
2019-09-04 14:10:29 +02:00
$this->staticTypeMapper = $staticTypeMapper;
$this->nodeFactory = $nodeFactory;
$this->phpDocTypeChanger = $phpDocTypeChanger;
$this->phpDocInfoFactory = $phpDocInfoFactory;
}
public function createFactoryClass(\PhpParser\Node\Stmt\Class_ $class, \PHPStan\Type\ObjectType $objectType) : \PhpParser\Node\Stmt\Class_
{
$className = $this->nodeNameResolver->getName($class);
2019-09-04 14:10:29 +02:00
if ($className === null) {
throw new \Rector\Core\Exception\ShouldNotHappenException();
2019-09-04 14:10:29 +02:00
}
$name = $className . 'Factory';
$shortName = $this->resolveClassShortName($name);
$factoryClassBuilder = new \RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\ClassBuilder($shortName);
2019-09-04 14:10:29 +02:00
$factoryClassBuilder->makeFinal();
$properties = $this->createPropertiesFromTypes($objectType);
$factoryClassBuilder->addStmts($properties);
// constructor
$constructorClassMethod = $this->createConstructMethod($objectType);
$factoryClassBuilder->addStmt($constructorClassMethod);
// create
$classMethod = $this->createCreateMethod($class, $className, $properties);
$factoryClassBuilder->addStmt($classMethod);
return $factoryClassBuilder->getNode();
}
private function resolveClassShortName(string $name) : string
{
if (\RectorPrefix20210510\Nette\Utils\Strings::contains($name, '\\')) {
return (string) \RectorPrefix20210510\Nette\Utils\Strings::after($name, '\\', -1);
}
return $name;
}
/**
* @return Property[]
*/
private function createPropertiesFromTypes(\PHPStan\Type\ObjectType $objectType) : array
{
$properties = [];
$properties[] = $this->createPropertyFromObjectType($objectType);
return $properties;
}
private function createConstructMethod(\PHPStan\Type\ObjectType $objectType) : \PhpParser\Node\Stmt\ClassMethod
{
2019-09-04 14:10:29 +02:00
$propertyName = $this->propertyNaming->fqnToVariableName($objectType);
$paramBuilder = new \RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\ParamBuilder($propertyName);
2019-09-04 14:10:29 +02:00
$typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($objectType);
2019-11-05 12:44:49 +01:00
if ($typeNode !== null) {
2019-09-04 14:10:29 +02:00
$paramBuilder->setType($typeNode);
}
2019-09-04 14:10:29 +02:00
$params = [$paramBuilder->getNode()];
$assigns = $this->createAssignsFromParams($params);
$methodBuilder = new \RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder(\Rector\Core\ValueObject\MethodName::CONSTRUCT);
2019-09-04 14:10:29 +02:00
$methodBuilder->makePublic();
$methodBuilder->addParams($params);
$methodBuilder->addStmts($assigns);
return $methodBuilder->getNode();
}
/**
* @param Property[] $properties
*/
private function createCreateMethod(\PhpParser\Node\Stmt\Class_ $class, string $className, array $properties) : \PhpParser\Node\Stmt\ClassMethod
{
$new = new \PhpParser\Node\Expr\New_(new \PhpParser\Node\Name\FullyQualified($className));
$constructClassMethod = $class->getMethod(\Rector\Core\ValueObject\MethodName::CONSTRUCT);
$params = [];
if ($constructClassMethod !== null) {
foreach ($constructClassMethod->params as $param) {
$params[] = $param;
$new->args[] = new \PhpParser\Node\Arg($param->var);
}
}
foreach ($properties as $property) {
$propertyName = $this->nodeNameResolver->getName($property);
$propertyFetch = new \PhpParser\Node\Expr\PropertyFetch(new \PhpParser\Node\Expr\Variable('this'), $propertyName);
$new->args[] = new \PhpParser\Node\Arg($propertyFetch);
}
$return = new \PhpParser\Node\Stmt\Return_($new);
$methodBuilder = new \RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder('create');
$methodBuilder->setReturnType(new \PhpParser\Node\Name\FullyQualified($className));
2020-07-19 23:36:10 +02:00
$methodBuilder->makePublic();
$methodBuilder->addStmt($return);
$methodBuilder->addParams($params);
return $methodBuilder->getNode();
}
private function createPropertyFromObjectType(\PHPStan\Type\ObjectType $objectType) : \PhpParser\Node\Stmt\Property
{
$propertyName = $this->propertyNaming->fqnToVariableName($objectType);
$property = $this->nodeFactory->createPrivateProperty($propertyName);
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$this->phpDocTypeChanger->changeVarType($phpDocInfo, $objectType);
return $property;
}
/**
* @param Param[] $params
*
* @return Assign[]
*/
private function createAssignsFromParams(array $params) : array
{
$assigns = [];
/** @var Param $param */
foreach ($params as $param) {
$propertyFetch = new \PhpParser\Node\Expr\PropertyFetch(new \PhpParser\Node\Expr\Variable('this'), $param->var->name);
$assigns[] = new \PhpParser\Node\Expr\Assign($propertyFetch, new \PhpParser\Node\Expr\Variable($param->var->name));
}
return $assigns;
}
}