nodeNameResolver = $nodeNameResolver; $this->propertyNaming = $propertyNaming; $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); if ($className === null) { throw new \Rector\Core\Exception\ShouldNotHappenException(); } $name = $className . 'Factory'; $shortName = $this->resolveClassShortName($name); $factoryClassBuilder = new \RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\ClassBuilder($shortName); $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 { $propertyName = $this->propertyNaming->fqnToVariableName($objectType); $paramBuilder = new \RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\ParamBuilder($propertyName); $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($objectType); if ($typeNode !== null) { $paramBuilder->setType($typeNode); } $params = [$paramBuilder->getNode()]; $assigns = $this->createAssignsFromParams($params); $methodBuilder = new \RectorPrefix20210510\Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder(\Rector\Core\ValueObject\MethodName::CONSTRUCT); $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)); $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; } }