[Reconstructor] add ContructorMethodBuilder, relieve InjectReconstructor

This commit is contained in:
TomasVotruba 2017-07-16 13:13:24 +02:00
parent dd41b45b04
commit 57f291a389
6 changed files with 110 additions and 56 deletions

View File

@ -0,0 +1,72 @@
<?php declare(strict_types=1);
namespace Rector\Builder;
use PhpParser\Builder\Method;
use PhpParser\Builder\Param;
use PhpParser\BuilderFactory;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Parser;
final class ConstructorMethodBuilder
{
/**
* @var Parser
*/
private $parser;
/**
* @var BuilderFactory
*/
private $builderFactory;
public function __construct(Parser $parser, BuilderFactory $builderFactory)
{
$this->parser = $parser;
$this->builderFactory = $builderFactory;
}
public function addPropertyAssignToClass(Class_ $classNode, string $propertyType, string $propertyName): void
{
$assign = $this->createPropertyAssignment($propertyName);
$constructorMethod = $classNode->getMethod('__construct') ?: null;
/** @var ClassMethod $constructorMethod */
if ($constructorMethod) {
$constructorMethod->params[] = $this->createParameter($propertyType, $propertyName)
->getNode();
$constructorMethod->stmts[] = $assign[0];
return;
}
/** @var Method $constructorMethod */
$constructorMethod = $this->builderFactory->method('__construct')
->makePublic()
->addParam($this->createParameter($propertyType, $propertyName))
->addStmts($assign);
$classNode->stmts[] = $constructorMethod->getNode();
}
private function createParameter(string $propertyType, string $propertyName): Param
{
return $this->builderFactory->param($propertyName)
->setTypeHint($propertyType);
}
/**
* @return Node[]
*/
private function createPropertyAssignment(string $propertyName): array
{
return $this->parser->parse(sprintf(
'<?php $this->%s = $%s;',
$propertyName,
$propertyName
));
}
}

View File

@ -2,32 +2,25 @@
namespace Rector\Reconstructor\DependencyInjection;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpParser\Builder\Method;
use PhpParser\BuilderFactory;
use PhpParser\Comment\Doc;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use PhpParser\Parser;
use Rector\Builder\ConstructorMethodBuilder;
use Rector\Contract\Dispatcher\ReconstructorInterface;
final class InjectAnnotationToConstructorReconstructor implements ReconstructorInterface
{
/**
* @var BuilderFactory
* @var ConstructorMethodBuilder
*/
private $builderFactory;
/**
* @var Parser
*/
private $parser;
private $constructorMethodBuilder;
public function __construct(BuilderFactory $builderFactory, Parser $parser)
public function __construct(ConstructorMethodBuilder $constructorMethodBuilder)
{
$this->builderFactory = $builderFactory;
$this->parser = $parser;
$this->constructorMethodBuilder = $constructorMethodBuilder;
}
public function isCandidate(Node $node): bool
@ -52,45 +45,12 @@ final class InjectAnnotationToConstructorReconstructor implements ReconstructorI
continue;
}
$this->removeInjectAnnotation($injectAnnotations, $propertyNode, $propertyDocBlock);
$this->makePropertyPrivate($propertyNode);
$propertyType = $propertyDocBlock->getAnnotationsOfType('var')[0]->getTypes()[0];
// 1. remove @inject annotation
foreach ($injectAnnotations as $injectAnnotation) {
$injectAnnotation->remove();
}
$propertyNode->setDocComment(new Doc($propertyDocBlock->getContent()));
// 2. make public property private
$propertyNode->flags = Class_::MODIFIER_PRIVATE;
$propertyName = $propertyNode->props[0]->name;
// build assignment for constructor method body
/** @var Node[] $assign */
$assign = $this->parser->parse(sprintf(
'<?php $this->%s = $%s;',
$propertyName,
$propertyName
));
$constructorMethod = $classNode->getMethod('__construct') ?: null;
/** @var ClassMethod $constructorMethod */
if ($constructorMethod) {
$constructorMethod->params[] = $this->builderFactory->param($propertyName)
->setTypeHint($propertyType)->getNode();
$constructorMethod->stmts[] = $assign[0];
} else {
/** @var Method $constructorMethod */
$constructorMethod = $this->builderFactory->method('__construct')
->makePublic()
->addParam($this->builderFactory->param($propertyName)
->setTypeHint($propertyType)
)
->addStmts($assign);
$classNode->stmts[] = $constructorMethod->getNode();
}
$this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $propertyType, $propertyName);
}
}
@ -98,4 +58,25 @@ final class InjectAnnotationToConstructorReconstructor implements ReconstructorI
{
return new DocBlock($propertyNode->getDocComment());
}
/**
* @param $propertyNode
* @return int
*/
private function makePropertyPrivate($propertyNode): int
{
return $propertyNode->flags = Class_::MODIFIER_PRIVATE;
}
/**
* @param Annotation[] $injectAnnotations
*/
private function removeInjectAnnotation(array $injectAnnotations, Property $propertyNode, DocBlock $propertyDocBlock): void
{
foreach ($injectAnnotations as $injectAnnotation) {
$injectAnnotation->remove();
}
$propertyNode->setDocComment(new Doc($propertyDocBlock->getContent()));
}
}

View File

@ -15,6 +15,3 @@ services:
factory: ['@Rector\Parser\ParserFactory', 'create']
PhpParser\BuilderFactory: ~
PhpParser\PrettyPrinter\Standard: ~
# $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
# $statements = $parser->parse(file_get_contents(__DIR__ . '/../source/ClassWithInjects

View File

@ -1,15 +1,18 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Reconstructor\DependencyInjection;
namespace Rector\Tests\Reconstructor\DependencyInjection\InjectAnnotationToConstructorReconstructor;
use Rector\Reconstructor\DependencyInjection\InjectAnnotationToConstructorReconstructor;
use Rector\Testing\PHPUnit\AbstractReconstructorTestCase;
final class InjectAnnotationToConstructorReconstructorTest extends AbstractReconstructorTestCase
final class Test extends AbstractReconstructorTestCase
{
public function test(): void
{
$this->doTestFileMatchesExpectedContent(__DIR__ . '/wrong/wrong.php.inc', __DIR__ . '/correct/correct.php.inc');
$this->doTestFileMatchesExpectedContent(
__DIR__ . '/wrong/wrong.php.inc',
__DIR__ . '/correct/correct.php.inc'
);
}
protected function getReconstructorClass(): string
@ -17,3 +20,4 @@ final class InjectAnnotationToConstructorReconstructorTest extends AbstractRecon
return InjectAnnotationToConstructorReconstructor::class;
}
}