mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-01 00:40:52 +00:00
Merge pull request #2 from TomasVotruba/new-rector-named-services
[Symfony] New Rector: named services in Symfony => constructor injection
This commit is contained in:
commit
2b86f6497a
|
@ -9,7 +9,8 @@ This tool will *reconstruct* (change) your code - **run it only in a new clean g
|
||||||
|
|
||||||
## All Reconstructors
|
## All Reconstructors
|
||||||
|
|
||||||
- `InjectAnnotationToConstructorReconstructor`
|
- `InjectAnnotationToConstructorNodeTraverser` ([Nette](https://github.com/nette/))
|
||||||
|
- `NamedServicesToConstructorNodeTraverser` ([Symfony](https://github.com/symfony/))
|
||||||
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
|
@ -10,13 +10,14 @@
|
||||||
"php": "^7.1",
|
"php": "^7.1",
|
||||||
"symfony/console": "^3.3",
|
"symfony/console": "^3.3",
|
||||||
"symfony/dependency-injection": "^3.3",
|
"symfony/dependency-injection": "^3.3",
|
||||||
"nikic/php-parser": "^3.0",
|
"nikic/php-parser": "4.0.x-dev as 3.0.2",
|
||||||
"ocramius/code-generator-utils": "^0.4.1"
|
"ocramius/code-generator-utils": "^0.4",
|
||||||
|
"nette/utils": "^2.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^6.2",
|
"phpunit/phpunit": "^6.2",
|
||||||
"tracy/tracy": "^2.4",
|
"tracy/tracy": "^2.4",
|
||||||
"symplify/easy-coding-standard": "^2.1",
|
"symplify/easy-coding-standard": "@dev",
|
||||||
"phpstan/phpstan": "^0.7"
|
"phpstan/phpstan": "^0.7"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -33,6 +34,6 @@
|
||||||
"all": ["phpunit", "@cs", "@ps"],
|
"all": ["phpunit", "@cs", "@ps"],
|
||||||
"cs": "ecs check src tests",
|
"cs": "ecs check src tests",
|
||||||
"fs": "ecs check src tests --fix",
|
"fs": "ecs check src tests --fix",
|
||||||
"ps": "phpstan analyse src tests --level 7 -c phpstan.neon"
|
"ps": "phpstan analyse src tests --level 7 --configuration phpstan.neon"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
easy-coding-standard.neon
Normal file
3
easy-coding-standard.neon
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
includes:
|
||||||
|
# todo:
|
||||||
|
# - vendor/symplify/easy-coding-standard/config/
|
|
@ -1,5 +1,4 @@
|
||||||
parameters:
|
parameters:
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
- '#should return PhpParser\\Node\[\] but returns PhpParser\\Node\[\]\|null#'
|
- '#Internal error#'
|
||||||
# annotated => bug
|
# - '#should return PhpParser\\Node\[\] but returns PhpParser\\Node\[\]\|null#'
|
||||||
- '#Access to an undefined property PhpParser\\Node\\Expr::\$value#'
|
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
namespace Rector\Application;
|
namespace Rector\Application;
|
||||||
|
|
||||||
|
use PhpParser\Lexer;
|
||||||
|
use PhpParser\NodeTraverser;
|
||||||
use PhpParser\Parser;
|
use PhpParser\Parser;
|
||||||
use Rector\Dispatcher\NodeDispatcher;
|
|
||||||
use Rector\Printer\CodeStyledPrinter;
|
use Rector\Printer\CodeStyledPrinter;
|
||||||
use SplFileInfo;
|
use SplFileInfo;
|
||||||
|
|
||||||
|
@ -14,21 +15,27 @@ final class FileProcessor
|
||||||
*/
|
*/
|
||||||
private $parser;
|
private $parser;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var NodeDispatcher
|
|
||||||
*/
|
|
||||||
private $nodeDispatcher;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var CodeStyledPrinter
|
* @var CodeStyledPrinter
|
||||||
*/
|
*/
|
||||||
private $codeStyledPrinter;
|
private $codeStyledPrinter;
|
||||||
|
|
||||||
public function __construct(Parser $parser, CodeStyledPrinter $codeStyledPrinter, NodeDispatcher $nodeDispatcher)
|
/**
|
||||||
|
* @var NodeTraverser
|
||||||
|
*/
|
||||||
|
private $nodeTraverser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Lexer
|
||||||
|
*/
|
||||||
|
private $lexer;
|
||||||
|
|
||||||
|
public function __construct(Parser $parser, CodeStyledPrinter $codeStyledPrinter, Lexer $lexer, NodeTraverser $nodeTraverser)
|
||||||
{
|
{
|
||||||
$this->parser = $parser;
|
$this->parser = $parser;
|
||||||
$this->nodeDispatcher = $nodeDispatcher;
|
|
||||||
$this->codeStyledPrinter = $codeStyledPrinter;
|
$this->codeStyledPrinter = $codeStyledPrinter;
|
||||||
|
$this->nodeTraverser = $nodeTraverser;
|
||||||
|
$this->lexer = $lexer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,18 +51,18 @@ final class FileProcessor
|
||||||
public function processFile(SplFileInfo $file): void
|
public function processFile(SplFileInfo $file): void
|
||||||
{
|
{
|
||||||
$fileContent = file_get_contents($file->getRealPath());
|
$fileContent = file_get_contents($file->getRealPath());
|
||||||
$nodes = $this->parser->parse($fileContent);
|
$oldStmts = $this->parser->parse($fileContent);
|
||||||
if ($nodes === null) {
|
if ($oldStmts === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$originalNodes = $this->cloneArrayOfObjects($nodes);
|
$oldStmts = $this->cloneArrayOfObjects($oldStmts);
|
||||||
|
|
||||||
foreach ($nodes as $node) {
|
$oldTokens = $this->lexer->getTokens();
|
||||||
$this->nodeDispatcher->dispatch($node);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->codeStyledPrinter->printToFile($file, $originalNodes, $nodes);
|
$newStmts = $this->nodeTraverser->traverse($oldStmts);
|
||||||
|
|
||||||
|
$this->codeStyledPrinter->printToFile($file, $newStmts, $oldStmts, $oldTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
26
src/Builder/Class_/ClassPropertyCollector.php
Normal file
26
src/Builder/Class_/ClassPropertyCollector.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\Builder\Class_;
|
||||||
|
|
||||||
|
final class ClassPropertyCollector
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string[][]
|
||||||
|
*/
|
||||||
|
private $classProperties = [];
|
||||||
|
|
||||||
|
public function addPropertyForClass(string $class, string $propertyType, string $propertyName): void
|
||||||
|
{
|
||||||
|
$this->classProperties[$class] = [
|
||||||
|
$propertyType => $propertyName
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getPropertiesforClass(string $class): array
|
||||||
|
{
|
||||||
|
return $this->classProperties[$class] ?? [];
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Rector\Builder;
|
namespace Rector\Builder;
|
||||||
|
|
||||||
|
use Nette\Utils\Arrays;
|
||||||
use PhpParser\Builder\Method;
|
use PhpParser\Builder\Method;
|
||||||
use PhpParser\Builder\Param;
|
use PhpParser\Builder\Param;
|
||||||
use PhpParser\BuilderFactory;
|
use PhpParser\BuilderFactory;
|
||||||
|
@ -49,7 +50,7 @@ final class ConstructorMethodBuilder
|
||||||
->addParam($this->createParameter($propertyType, $propertyName))
|
->addParam($this->createParameter($propertyType, $propertyName))
|
||||||
->addStmts($assign);
|
->addStmts($assign);
|
||||||
|
|
||||||
$classNode->stmts[] = $constructorMethod->getNode();
|
$this->addAsFirstMethod($classNode, $constructorMethod->getNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createParameter(string $propertyType, string $propertyName): Param
|
private function createParameter(string $propertyType, string $propertyName): Param
|
||||||
|
@ -69,4 +70,21 @@ final class ConstructorMethodBuilder
|
||||||
$propertyName
|
$propertyName
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private function addAsFirstMethod(Class_ $classNode, ClassMethod $constructorMethod): void
|
||||||
|
{
|
||||||
|
foreach ($classNode->stmts as $key => $classElementNode) {
|
||||||
|
if ($classElementNode instanceof ClassMethod) {
|
||||||
|
Arrays::insertBefore(
|
||||||
|
$classNode->stmts,
|
||||||
|
$key,
|
||||||
|
['before_' . $key => $constructorMethod]
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$classNode->stmts[] = $constructorMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
29
src/Builder/Kernel/ServiceFromKernelResolver.php
Normal file
29
src/Builder/Kernel/ServiceFromKernelResolver.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\Builder\Kernel;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Kernel;
|
||||||
|
|
||||||
|
final class ServiceFromKernelResolver
|
||||||
|
{
|
||||||
|
public function resolveServiceClassByNameFromKernel(string $serviceName, string $kernelClass): ?string
|
||||||
|
{
|
||||||
|
/** @var Kernel $kernel */
|
||||||
|
$kernel = new $kernelClass('dev', true);
|
||||||
|
$kernel->boot();
|
||||||
|
|
||||||
|
// @todo: cache
|
||||||
|
// @todo: initialize without creating cache or log directory
|
||||||
|
// @todo: call only loadBundles() and initializeContainer() methods
|
||||||
|
|
||||||
|
$container = $kernel->getContainer();
|
||||||
|
if (! $container->has($serviceName)) {
|
||||||
|
// service name could not be found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$service = $container->get($serviceName);
|
||||||
|
|
||||||
|
return get_class($service);
|
||||||
|
}
|
||||||
|
}
|
14
src/Builder/Naming/NameResolver.php
Normal file
14
src/Builder/Naming/NameResolver.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\Builder\Naming;
|
||||||
|
|
||||||
|
final class NameResolver
|
||||||
|
{
|
||||||
|
public function resolvePropertyNameFromType(string $serviceType): string
|
||||||
|
{
|
||||||
|
$serviceNameParts = explode('\\', $serviceType);
|
||||||
|
$lastNamePart = array_pop($serviceNameParts);
|
||||||
|
|
||||||
|
return lcfirst($lastNamePart);
|
||||||
|
}
|
||||||
|
}
|
77
src/Builder/PropertyBuilder.php
Normal file
77
src/Builder/PropertyBuilder.php
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\Builder;
|
||||||
|
|
||||||
|
use Nette\Utils\Arrays;
|
||||||
|
use PhpParser\BuilderFactory;
|
||||||
|
use PhpParser\Comment\Doc;
|
||||||
|
use PhpParser\Node\Stmt\Class_;
|
||||||
|
use PhpParser\Node\Stmt\ClassMethod;
|
||||||
|
use PhpParser\Node\Stmt\Property;
|
||||||
|
|
||||||
|
final class PropertyBuilder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var BuilderFactory
|
||||||
|
*/
|
||||||
|
private $builderFactory;
|
||||||
|
|
||||||
|
public function __construct(BuilderFactory $builderFactory)
|
||||||
|
{
|
||||||
|
$this->builderFactory = $builderFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addPropertyToClass(Class_ $classNode, string $propertyType, string $propertyName): void
|
||||||
|
{
|
||||||
|
$propertyNode = $this->buildPrivatePropertyNode($propertyType, $propertyName);
|
||||||
|
|
||||||
|
// add before first method
|
||||||
|
foreach ($classNode->stmts as $key => $classElementNode) {
|
||||||
|
if ($classElementNode instanceof ClassMethod) {
|
||||||
|
Arrays::insertBefore(
|
||||||
|
$classNode->stmts,
|
||||||
|
$key,
|
||||||
|
['before_' . $key => $propertyNode]
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// or after last property
|
||||||
|
$previousElement = null;
|
||||||
|
foreach ($classNode->stmts as $key => $classElementNode) {
|
||||||
|
if ($previousElement instanceof Property && ! $classElementNode instanceof Property) {
|
||||||
|
Arrays::insertBefore(
|
||||||
|
$classNode->stmts,
|
||||||
|
$key,
|
||||||
|
['before_' . $key => $propertyNode]
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$previousElement = $classElementNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
$classNode->stmts[] = $propertyNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildPrivatePropertyNode(string $propertyType, string $propertyName): Property
|
||||||
|
{
|
||||||
|
$docComment = $this->createDocWithVarAnnotation($propertyType);
|
||||||
|
|
||||||
|
$propertyBuilder = $this->builderFactory->property($propertyName)
|
||||||
|
->makePrivate()
|
||||||
|
->setDocComment($docComment);
|
||||||
|
|
||||||
|
return $propertyBuilder->getNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createDocWithVarAnnotation(string $propertyType): Doc
|
||||||
|
{
|
||||||
|
return new Doc('/**'
|
||||||
|
. PHP_EOL . ' * @var ' . $propertyType
|
||||||
|
. PHP_EOL . ' */');
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,8 +38,6 @@ final class ReconstructCommand extends Command
|
||||||
{
|
{
|
||||||
$this->setName(self::NAME);
|
$this->setName(self::NAME);
|
||||||
$this->setDescription('Reconstruct set of your code.');
|
$this->setDescription('Reconstruct set of your code.');
|
||||||
|
|
||||||
// @todo: use modular configure from ApiGen
|
|
||||||
$this->addArgument(
|
$this->addArgument(
|
||||||
self::ARGUMENT_SOURCE_NAME,
|
self::ARGUMENT_SOURCE_NAME,
|
||||||
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
|
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Rector\Contract\Dispatcher;
|
|
||||||
|
|
||||||
use PhpParser\Node;
|
|
||||||
|
|
||||||
interface ReconstructorInterface
|
|
||||||
{
|
|
||||||
public function isCandidate(Node $node): bool;
|
|
||||||
|
|
||||||
public function reconstruct(Node $classNode): void;
|
|
||||||
}
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace Rector\DependencyInjection\CompilerPass;
|
namespace Rector\DependencyInjection\CompilerPass;
|
||||||
|
|
||||||
use Rector\Contract\Dispatcher\ReconstructorInterface;
|
use PhpParser\NodeTraverser;
|
||||||
use Rector\Dispatcher\NodeDispatcher;
|
use PhpParser\NodeVisitor;
|
||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
@ -15,7 +15,7 @@ final class CollectorCompilerPass implements CompilerPassInterface
|
||||||
public function process(ContainerBuilder $containerBuilder): void
|
public function process(ContainerBuilder $containerBuilder): void
|
||||||
{
|
{
|
||||||
$this->collectCommandsToConsoleApplication($containerBuilder);
|
$this->collectCommandsToConsoleApplication($containerBuilder);
|
||||||
$this->collectReconstructorsToNodeDispatcher($containerBuilder);
|
$this->collectNodeVisitorsToTraverser($containerBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function collectCommandsToConsoleApplication(ContainerBuilder $containerBuilder): void
|
private function collectCommandsToConsoleApplication(ContainerBuilder $containerBuilder): void
|
||||||
|
@ -28,13 +28,13 @@ final class CollectorCompilerPass implements CompilerPassInterface
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function collectReconstructorsToNodeDispatcher(ContainerBuilder $containerBuilder): void
|
private function collectNodeVisitorsToTraverser(ContainerBuilder $containerBuilder): void
|
||||||
{
|
{
|
||||||
DefinitionCollector::loadCollectorWithType(
|
DefinitionCollector::loadCollectorWithType(
|
||||||
$containerBuilder,
|
$containerBuilder,
|
||||||
NodeDispatcher::class,
|
NodeTraverser::class,
|
||||||
ReconstructorInterface::class,
|
NodeVisitor::class,
|
||||||
'addReconstructor'
|
'addVisitor'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Rector\Dispatcher;
|
|
||||||
|
|
||||||
use PhpParser\Node;
|
|
||||||
use Rector\Contract\Dispatcher\ReconstructorInterface;
|
|
||||||
|
|
||||||
final class NodeDispatcher
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var ReconstructorInterface[]
|
|
||||||
*/
|
|
||||||
private $reconstructors;
|
|
||||||
|
|
||||||
public function addReconstructor(ReconstructorInterface $reconstructor): void
|
|
||||||
{
|
|
||||||
$this->reconstructors[] = $reconstructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function dispatch(Node $node): void
|
|
||||||
{
|
|
||||||
// todo: build hash map
|
|
||||||
foreach ($this->reconstructors as $reconstructor) {
|
|
||||||
if ($reconstructor->isCandidate($node)) {
|
|
||||||
$reconstructor->reconstruct($node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
22
src/NodeTraverser/StateHolder.php
Normal file
22
src/NodeTraverser/StateHolder.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\NodeTraverser;
|
||||||
|
|
||||||
|
final class StateHolder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $isAfterTraverseCalled = false;
|
||||||
|
|
||||||
|
public function setAfterTraverserIsCalled(): void
|
||||||
|
{
|
||||||
|
$this->isAfterTraverseCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAfterTraverseCalled(): bool
|
||||||
|
{
|
||||||
|
return $this->isAfterTraverseCalled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
namespace Rector\Reconstructor\DependencyInjection;
|
namespace Rector\NodeVisitor\DependencyInjection;
|
||||||
|
|
||||||
use PhpCsFixer\DocBlock\DocBlock;
|
use PhpCsFixer\DocBlock\DocBlock;
|
||||||
use PhpParser\Comment\Doc;
|
use PhpParser\Comment\Doc;
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Stmt\Class_;
|
use PhpParser\Node\Stmt\Class_;
|
||||||
use PhpParser\Node\Stmt\Property;
|
use PhpParser\Node\Stmt\Property;
|
||||||
|
use PhpParser\NodeVisitorAbstract;
|
||||||
use Rector\Builder\ConstructorMethodBuilder;
|
use Rector\Builder\ConstructorMethodBuilder;
|
||||||
use Rector\Contract\Dispatcher\ReconstructorInterface;
|
|
||||||
|
|
||||||
final class InjectAnnotationToConstructorReconstructor implements ReconstructorInterface
|
final class InjectAnnotationToConstructorNodeVisitor extends NodeVisitorAbstract
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private const ANNOTATION_INJECT = 'inject';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ConstructorMethodBuilder
|
* @var ConstructorMethodBuilder
|
||||||
*/
|
*/
|
||||||
|
@ -28,9 +33,31 @@ final class InjectAnnotationToConstructorReconstructor implements ReconstructorI
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Class_ $classNode
|
* Called when entering a node.
|
||||||
|
*
|
||||||
|
* Return value semantics:
|
||||||
|
* * null
|
||||||
|
* => $node stays as-is
|
||||||
|
* * NodeTraverser::DONT_TRAVERSE_CHILDREN
|
||||||
|
* => Children of $node are not traversed. $node stays as-is
|
||||||
|
* * NodeTraverser::STOP_TRAVERSAL
|
||||||
|
* => Traversal is aborted. $node stays as-is
|
||||||
|
* * otherwise
|
||||||
|
* => $node is set to the return value
|
||||||
|
*
|
||||||
|
* @return null|int|Node Replacement node (or special return value)
|
||||||
*/
|
*/
|
||||||
public function reconstruct(Node $classNode): void
|
public function enterNode(Node $node)
|
||||||
|
{
|
||||||
|
if ($node instanceof Class_) {
|
||||||
|
$this->reconstruct($node);
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function reconstruct(Class_ $classNode): void
|
||||||
{
|
{
|
||||||
foreach ($classNode->stmts as $classElementStatement) {
|
foreach ($classNode->stmts as $classElementStatement) {
|
||||||
if (! $classElementStatement instanceof Property) {
|
if (! $classElementStatement instanceof Property) {
|
||||||
|
@ -39,7 +66,7 @@ final class InjectAnnotationToConstructorReconstructor implements ReconstructorI
|
||||||
$propertyNode = $classElementStatement;
|
$propertyNode = $classElementStatement;
|
||||||
|
|
||||||
$propertyDocBlock = $this->createDocBlockFromProperty($propertyNode);
|
$propertyDocBlock = $this->createDocBlockFromProperty($propertyNode);
|
||||||
$injectAnnotations = $propertyDocBlock->getAnnotationsOfType('inject');
|
$injectAnnotations = $propertyDocBlock->getAnnotationsOfType(self::ANNOTATION_INJECT);
|
||||||
if (! $injectAnnotations) {
|
if (! $injectAnnotations) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +76,8 @@ final class InjectAnnotationToConstructorReconstructor implements ReconstructorI
|
||||||
|
|
||||||
$propertyType = $propertyDocBlock->getAnnotationsOfType('var')[0]
|
$propertyType = $propertyDocBlock->getAnnotationsOfType('var')[0]
|
||||||
->getTypes()[0];
|
->getTypes()[0];
|
||||||
$propertyName = $propertyNode->props[0]->name;
|
$propertyName = (string) $propertyNode->props[0]->name;
|
||||||
|
|
||||||
$this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $propertyType, $propertyName);
|
$this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $propertyType, $propertyName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +94,7 @@ final class InjectAnnotationToConstructorReconstructor implements ReconstructorI
|
||||||
|
|
||||||
private function removeInjectAnnotationFromProperty(Property $propertyNode, DocBlock $propertyDocBlock): void
|
private function removeInjectAnnotationFromProperty(Property $propertyNode, DocBlock $propertyDocBlock): void
|
||||||
{
|
{
|
||||||
$injectAnnotations = $propertyDocBlock->getAnnotationsOfType('inject');
|
$injectAnnotations = $propertyDocBlock->getAnnotationsOfType(self::ANNOTATION_INJECT);
|
||||||
|
|
||||||
foreach ($injectAnnotations as $injectAnnotation) {
|
foreach ($injectAnnotations as $injectAnnotation) {
|
||||||
$injectAnnotation->remove();
|
$injectAnnotation->remove();
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\NodeVisitor\DependencyInjection\NamedServicesToConstructor;
|
||||||
|
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Stmt\Class_;
|
||||||
|
use PhpParser\NodeVisitorAbstract;
|
||||||
|
use Rector\Builder\Class_\ClassPropertyCollector;
|
||||||
|
use Rector\Builder\ConstructorMethodBuilder;
|
||||||
|
use Rector\Builder\PropertyBuilder;
|
||||||
|
use Rector\NodeTraverser\StateHolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add new propertis to class and to contructor.
|
||||||
|
*/
|
||||||
|
final class AddPropertiesToClassNodeVisitor extends NodeVisitorAbstract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ConstructorMethodBuilder
|
||||||
|
*/
|
||||||
|
private $constructorMethodBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PropertyBuilder
|
||||||
|
*/
|
||||||
|
private $propertyBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClassPropertyCollector
|
||||||
|
*/
|
||||||
|
private $newClassPropertyCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var StateHolder
|
||||||
|
*/
|
||||||
|
private $stateHolder;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
ConstructorMethodBuilder $constructorMethodBuilder,
|
||||||
|
PropertyBuilder $propertyBuilder,
|
||||||
|
ClassPropertyCollector $newClassPropertyCollector,
|
||||||
|
StateHolder $stateHolder
|
||||||
|
) {
|
||||||
|
$this->constructorMethodBuilder = $constructorMethodBuilder;
|
||||||
|
$this->propertyBuilder = $propertyBuilder;
|
||||||
|
$this->newClassPropertyCollector = $newClassPropertyCollector;
|
||||||
|
$this->stateHolder = $stateHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Node[] $nodes
|
||||||
|
* @return Node[]
|
||||||
|
*/
|
||||||
|
public function afterTraverse(array $nodes): array
|
||||||
|
{
|
||||||
|
foreach ($nodes as $node) {
|
||||||
|
if ($node instanceof Class_) {
|
||||||
|
$this->reconstruct($node, (string) $node->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function reconstruct(Class_ $classNode, string $className): void
|
||||||
|
{
|
||||||
|
$propertiesForClass = $this->newClassPropertyCollector->getPropertiesforClass($className);
|
||||||
|
|
||||||
|
foreach ($propertiesForClass as $propertyType => $propertyName) {
|
||||||
|
$this->stateHolder->setAfterTraverserIsCalled();
|
||||||
|
$this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $propertyType, $propertyName);
|
||||||
|
$this->propertyBuilder->addPropertyToClass($classNode, $propertyType, $propertyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\NodeVisitor\DependencyInjection\NamedServicesToConstructor;
|
||||||
|
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Expr\Assign;
|
||||||
|
use PhpParser\Node\Expr\MethodCall;
|
||||||
|
use PhpParser\Node\Expr\PropertyFetch;
|
||||||
|
use PhpParser\Node\Expr\Variable;
|
||||||
|
use PhpParser\Node\Scalar\String_;
|
||||||
|
use PhpParser\NodeVisitorAbstract;
|
||||||
|
use Rector\Builder\Kernel\ServiceFromKernelResolver;
|
||||||
|
use Rector\Builder\Naming\NameResolver;
|
||||||
|
use Rector\Builder\Class_\ClassPropertyCollector;
|
||||||
|
use Rector\Tests\NodeVisitor\DependencyInjection\NamedServicesToConstructorReconstructor\Source\LocalKernel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts all:
|
||||||
|
* $this->get('some_service') # where "some_service" is name of the service in container
|
||||||
|
*
|
||||||
|
* into:
|
||||||
|
* $this->someService # where "someService" is type of the service
|
||||||
|
*/
|
||||||
|
final class GetterToPropertyNodeVisitor extends NodeVisitorAbstract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $className;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var NameResolver
|
||||||
|
*/
|
||||||
|
private $nameResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ServiceFromKernelResolver
|
||||||
|
*/
|
||||||
|
private $serviceFromKernelResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClassPropertyCollector
|
||||||
|
*/
|
||||||
|
private $classPropertyCollector;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
NameResolver $nameResolver,
|
||||||
|
ServiceFromKernelResolver $serviceFromKernelResolver,
|
||||||
|
ClassPropertyCollector $classPropertyCollector
|
||||||
|
) {
|
||||||
|
$this->nameResolver = $nameResolver;
|
||||||
|
$this->serviceFromKernelResolver = $serviceFromKernelResolver;
|
||||||
|
$this->classPropertyCollector = $classPropertyCollector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Node[] $nodes
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function beforeTraverse(array $nodes): ?array
|
||||||
|
{
|
||||||
|
foreach ($nodes as $node) {
|
||||||
|
if ($node instanceof Node\Stmt\Class_) {
|
||||||
|
$this->className = (string) $node->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return value semantics:
|
||||||
|
* * null
|
||||||
|
* => $node stays as-is
|
||||||
|
* * NodeTraverser::DONT_TRAVERSE_CHILDREN
|
||||||
|
* => Children of $node are not traversed. $node stays as-is
|
||||||
|
* * NodeTraverser::STOP_TRAVERSAL
|
||||||
|
* => Traversal is aborted. $node stays as-is
|
||||||
|
* * otherwise
|
||||||
|
* => $node is set to the return value
|
||||||
|
*
|
||||||
|
* @return null|int|Node
|
||||||
|
*/
|
||||||
|
public function enterNode(Node $node)
|
||||||
|
{
|
||||||
|
if ($this->isCandidate($node)) {
|
||||||
|
$this->reconstruct($node);
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isCandidate(Node $node): bool
|
||||||
|
{
|
||||||
|
// $var = $this->get('some_service');
|
||||||
|
// $var = $this->get('some_service')->getData();
|
||||||
|
if ($node instanceof Assign) {
|
||||||
|
if ($node->expr instanceof MethodCall || $node->var instanceof MethodCall) {
|
||||||
|
if ($this->isContainerGetCall($node->expr)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ['var => $this->get('some_service')->getData()]
|
||||||
|
if ($node instanceof MethodCall && $node->var instanceof MethodCall) {
|
||||||
|
if ($this->isContainerGetCall($node->var)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Assign|MethodCall $assignOrMethodCallNode
|
||||||
|
*/
|
||||||
|
private function reconstruct(Node $assignOrMethodCallNode): void
|
||||||
|
{
|
||||||
|
if ($assignOrMethodCallNode instanceof Assign) {
|
||||||
|
$refactoredMethodCall = $this->processMethodCallNode($assignOrMethodCallNode->expr);
|
||||||
|
if ($refactoredMethodCall) {
|
||||||
|
$assignOrMethodCallNode->expr = $refactoredMethodCall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($assignOrMethodCallNode instanceof MethodCall) {
|
||||||
|
$refactoredMethodCall = $this->processMethodCallNode($assignOrMethodCallNode->var);
|
||||||
|
if ($refactoredMethodCall) {
|
||||||
|
$assignOrMethodCallNode->var = $refactoredMethodCall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is "$this->get('string')" statements?
|
||||||
|
*/
|
||||||
|
private function isContainerGetCall(MethodCall $methodCall): bool
|
||||||
|
{
|
||||||
|
if ($methodCall->var->name !== 'this') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((string) $methodCall->name !== 'get') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $methodCall->args[0]->value instanceof String_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processMethodCallNode(MethodCall $methodCall): ?PropertyFetch
|
||||||
|
{
|
||||||
|
/** @var String_ $argument */
|
||||||
|
$argument = $methodCall->args[0]->value;
|
||||||
|
$serviceName = $argument->value;
|
||||||
|
|
||||||
|
$serviceType = $this->serviceFromKernelResolver->resolveServiceClassByNameFromKernel(
|
||||||
|
$serviceName, LocalKernel::class
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($serviceType === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$propertyName = $this->nameResolver->resolvePropertyNameFromType($serviceType);
|
||||||
|
|
||||||
|
$this->classPropertyCollector->addPropertyForClass($this->className, $serviceType, $propertyName);
|
||||||
|
|
||||||
|
return $this->createPropertyFetch($propertyName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates "$this->propertyName"
|
||||||
|
*/
|
||||||
|
private function createPropertyFetch(string $propertyName): PropertyFetch
|
||||||
|
{
|
||||||
|
return new PropertyFetch(
|
||||||
|
new Variable('this', [
|
||||||
|
'name' => $propertyName
|
||||||
|
]), $propertyName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
24
src/Parser/LexerFactory.php
Normal file
24
src/Parser/LexerFactory.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\Parser;
|
||||||
|
|
||||||
|
use PhpParser\Lexer;
|
||||||
|
use PhpParser\Lexer\Emulative;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Lexer allows Format-perserving AST Transformations
|
||||||
|
* @see https://github.com/nikic/PHP-Parser/issues/344#issuecomment-298162516
|
||||||
|
*/
|
||||||
|
final class LexerFactory
|
||||||
|
{
|
||||||
|
public function create(): Lexer
|
||||||
|
{
|
||||||
|
return new Emulative([
|
||||||
|
'usedAttributes' => [
|
||||||
|
'comments',
|
||||||
|
'startLine', 'endLine',
|
||||||
|
'startTokenPos', 'endTokenPos',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,13 +2,25 @@
|
||||||
|
|
||||||
namespace Rector\Parser;
|
namespace Rector\Parser;
|
||||||
|
|
||||||
|
use PhpParser\Lexer;
|
||||||
use PhpParser\Parser;
|
use PhpParser\Parser;
|
||||||
use PhpParser\ParserFactory as NikicParserFactory;
|
use PhpParser\ParserFactory as NikicParserFactory;
|
||||||
|
|
||||||
final class ParserFactory
|
final class ParserFactory
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var Lexer
|
||||||
|
*/
|
||||||
|
private $lexer;
|
||||||
|
|
||||||
|
public function __construct(Lexer $lexer)
|
||||||
|
{
|
||||||
|
$this->lexer = $lexer;
|
||||||
|
}
|
||||||
|
|
||||||
public function create(): Parser
|
public function create(): Parser
|
||||||
{
|
{
|
||||||
return (new NikicParserFactory)->create(NikicParserFactory::PREFER_PHP7);
|
$nikicParserFactory = new NikicParserFactory;
|
||||||
|
return $nikicParserFactory->create(NikicParserFactory::PREFER_PHP7, $this->lexer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,18 +17,18 @@ final class CodeStyledPrinter
|
||||||
$this->prettyPrinter = $prettyPrinter;
|
$this->prettyPrinter = $prettyPrinter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function printToFile(SplFileInfo $file, array $originalNodes, array $newNodes): void
|
public function printToFile(SplFileInfo $file, array $newStmts, array $oldStmts, array $oldTokens): void
|
||||||
{
|
{
|
||||||
if ($originalNodes === $newNodes) {
|
if ($oldStmts === $newStmts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_put_contents($file->getRealPath(), $this->printToString($newNodes));
|
file_put_contents($file->getRealPath(), $this->printToString($newStmts, $oldStmts, $oldTokens));
|
||||||
// @todo: run ecs with minimal set to code look nice
|
// @todo: run ecs with minimal set to code look nice
|
||||||
}
|
}
|
||||||
|
|
||||||
public function printToString(array $newNodes): string
|
public function printToString(array $newStmts, array $oldStmts, array $oldTokens): string
|
||||||
{
|
{
|
||||||
return '<?php ' . $this->prettyPrinter->prettyPrint($newNodes);
|
return $this->prettyPrinter->printFormatPreserving($newStmts, $oldStmts, $oldTokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
namespace Rector\Testing\Application;
|
namespace Rector\Testing\Application;
|
||||||
|
|
||||||
use PhpParser\Node;
|
use PhpParser\Lexer;
|
||||||
|
use PhpParser\NodeTraverser;
|
||||||
use PhpParser\Parser;
|
use PhpParser\Parser;
|
||||||
use Rector\Contract\Dispatcher\ReconstructorInterface;
|
use Rector\NodeTraverser\StateHolder;
|
||||||
use Rector\Printer\CodeStyledPrinter;
|
use Rector\Printer\CodeStyledPrinter;
|
||||||
use SplFileInfo;
|
use SplFileInfo;
|
||||||
|
|
||||||
|
@ -20,25 +21,48 @@ final class FileReconstructor
|
||||||
*/
|
*/
|
||||||
private $codeStyledPrinter;
|
private $codeStyledPrinter;
|
||||||
|
|
||||||
public function __construct(Parser $parser, CodeStyledPrinter $codeStyledPrinter)
|
/**
|
||||||
{
|
* @var Lexer
|
||||||
|
*/
|
||||||
|
private $lexer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var NodeTraverser
|
||||||
|
*/
|
||||||
|
private $nodeTraverser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var StateHolder
|
||||||
|
*/
|
||||||
|
private $stateHolder;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
Parser $parser,
|
||||||
|
CodeStyledPrinter $codeStyledPrinter,
|
||||||
|
Lexer $lexer,
|
||||||
|
NodeTraverser $nodeTraverser,
|
||||||
|
StateHolder $stateHolder
|
||||||
|
) {
|
||||||
$this->parser = $parser;
|
$this->parser = $parser;
|
||||||
$this->codeStyledPrinter = $codeStyledPrinter;
|
$this->codeStyledPrinter = $codeStyledPrinter;
|
||||||
|
$this->lexer = $lexer;
|
||||||
|
$this->nodeTraverser = $nodeTraverser;
|
||||||
|
$this->stateHolder = $stateHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processFileWithReconstructor(SplFileInfo $file, ReconstructorInterface $reconstructor): string
|
# ref: https://github.com/nikic/PHP-Parser/issues/344#issuecomment-298162516
|
||||||
|
public function processFile(SplFileInfo $file): string
|
||||||
{
|
{
|
||||||
$fileContent = file_get_contents($file->getRealPath());
|
$fileContent = file_get_contents($file->getRealPath());
|
||||||
|
|
||||||
/** @var Node[] $nodes */
|
$oldStmts = $this->parser->parse($fileContent);
|
||||||
$nodes = $this->parser->parse($fileContent);
|
$oldTokens = $this->lexer->getTokens();
|
||||||
|
$newStmts = $this->nodeTraverser->traverse($oldStmts);
|
||||||
|
|
||||||
foreach ($nodes as $node) {
|
if (! $this->stateHolder->isAfterTraverseCalled()) {
|
||||||
if ($reconstructor->isCandidate($node)) {
|
[$newStmts, $oldStmts] = [$oldStmts, $newStmts];
|
||||||
$reconstructor->reconstruct($node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->codeStyledPrinter->printToString($nodes);
|
return $this->codeStyledPrinter->printToString($newStmts, $oldStmts, $oldTokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
namespace Rector\Testing\PHPUnit;
|
namespace Rector\Testing\PHPUnit;
|
||||||
|
|
||||||
|
use PhpParser\NodeVisitor;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Rector\Contract\Dispatcher\ReconstructorInterface;
|
|
||||||
use Rector\DependencyInjection\ContainerFactory;
|
use Rector\DependencyInjection\ContainerFactory;
|
||||||
use Rector\Testing\Application\FileReconstructor;
|
use Rector\Testing\Application\FileReconstructor;
|
||||||
use SplFileInfo;
|
use SplFileInfo;
|
||||||
|
@ -29,17 +29,8 @@ abstract class AbstractReconstructorTestCase extends TestCase
|
||||||
|
|
||||||
protected function doTestFileMatchesExpectedContent(string $file, string $reconstructedFile): void
|
protected function doTestFileMatchesExpectedContent(string $file, string $reconstructedFile): void
|
||||||
{
|
{
|
||||||
$reconstructedFileContent = $this->fileReconstructor->processFileWithReconstructor(
|
$reconstructedFileContent = $this->fileReconstructor->processFile(new SplFileInfo($file));
|
||||||
new SplFileInfo($file), $this->getReconstructor()
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertStringEqualsFile($reconstructedFile, $reconstructedFileContent);
|
$this->assertStringEqualsFile($reconstructedFile, $reconstructedFileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected function getReconstructorClass(): string;
|
|
||||||
|
|
||||||
private function getReconstructor(): ReconstructorInterface
|
|
||||||
{
|
|
||||||
return $this->container->get($this->getReconstructorClass());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
parameters:
|
||||||
|
# todo
|
||||||
|
kernel_class: # for name based service refactoring
|
||||||
|
|
||||||
services:
|
services:
|
||||||
_defaults:
|
_defaults:
|
||||||
autowire: true
|
autowire: true
|
||||||
|
@ -13,5 +17,12 @@ services:
|
||||||
$name: "Rector"
|
$name: "Rector"
|
||||||
PhpParser\Parser:
|
PhpParser\Parser:
|
||||||
factory: ['@Rector\Parser\ParserFactory', 'create']
|
factory: ['@Rector\Parser\ParserFactory', 'create']
|
||||||
|
PhpParser\Lexer:
|
||||||
|
factory: ['@Rector\Parser\LexerFactory', 'create']
|
||||||
PhpParser\BuilderFactory: ~
|
PhpParser\BuilderFactory: ~
|
||||||
|
|
||||||
|
# Traverser
|
||||||
|
PhpParser\NodeTraverser: ~
|
||||||
|
# Printer
|
||||||
|
PhpParser\NodeVisitor\CloningVisitor: ~
|
||||||
PhpParser\PrettyPrinter\Standard: ~
|
PhpParser\PrettyPrinter\Standard: ~
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
namespace Rector\Tests\Reconstructor\DependencyInjection\InjectAnnotationToConstructorReconstructor;
|
namespace Rector\Tests\NodeVisitor\DependencyInjection\InjectAnnotationToConstructorReconstructor;
|
||||||
|
|
||||||
use Rector\Reconstructor\DependencyInjection\InjectAnnotationToConstructorReconstructor;
|
|
||||||
use Rector\Testing\PHPUnit\AbstractReconstructorTestCase;
|
use Rector\Testing\PHPUnit\AbstractReconstructorTestCase;
|
||||||
|
|
||||||
final class Test extends AbstractReconstructorTestCase
|
final class Test extends AbstractReconstructorTestCase
|
||||||
|
@ -14,10 +13,5 @@ final class Test extends AbstractReconstructorTestCase
|
||||||
__DIR__ . '/correct/correct.php.inc'
|
__DIR__ . '/correct/correct.php.inc'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getReconstructorClass(): string
|
|
||||||
{
|
|
||||||
return InjectAnnotationToConstructorReconstructor::class;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?php declare (strict_types=1);
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
class ClassWithInjects
|
class ClassWithInjects
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -14,4 +15,4 @@ class ClassWithInjects
|
||||||
$this->property = $property;
|
$this->property = $property;
|
||||||
$this->otherProperty = $otherProperty;
|
$this->otherProperty = $otherProperty;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\Tests\NodeVisitor\DependencyInjection\NamedServicesToConstructorReconstructor\Source;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||||
|
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
||||||
|
use Symfony\Component\HttpKernel\Kernel;
|
||||||
|
|
||||||
|
final class LocalKernel extends Kernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return BundleInterface[]
|
||||||
|
*/
|
||||||
|
public function registerBundles(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerContainerConfiguration(LoaderInterface $loader): void
|
||||||
|
{
|
||||||
|
$loader->load(__DIR__ . '/services.yml');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCacheDir(): string
|
||||||
|
{
|
||||||
|
return sys_get_temp_dir() . '/_rector_tests_local_kernel_cache';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogDir(): string
|
||||||
|
{
|
||||||
|
return sys_get_temp_dir() . '/_rector_tests_local_kernel_logs';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
services:
|
||||||
|
some.class:
|
||||||
|
class: stdClass
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\Tests\NodeVisitor\DependencyInjection\NamedServicesToConstructorReconstructor;
|
||||||
|
|
||||||
|
use Rector\Testing\PHPUnit\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/wrong2.php.inc',
|
||||||
|
__DIR__ . '/correct/correct2.php.inc'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->doTestFileMatchesExpectedContent(
|
||||||
|
__DIR__ . '/wrong/wrong3.php.inc',
|
||||||
|
__DIR__ . '/correct/correct3.php.inc'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
|
class ClassWithNamedService extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var stdClass
|
||||||
|
*/
|
||||||
|
private $stdClass;
|
||||||
|
public function __construct(stdClass $stdClass)
|
||||||
|
{
|
||||||
|
$this->stdClass = $stdClass;
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$this->stdClass->render();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
|
class ClassWithNamedService implements ContainerAwareInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var stdClass
|
||||||
|
*/
|
||||||
|
private $stdClass;
|
||||||
|
public function __construct(stdClass $stdClass)
|
||||||
|
{
|
||||||
|
$this->stdClass = $stdClass;
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$someService = $this->stdClass;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
|
class ClassWithNamedService implements ContainerAwareInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var stdClass
|
||||||
|
*/
|
||||||
|
private $stdClass;
|
||||||
|
public function __construct(stdClass $stdClass)
|
||||||
|
{
|
||||||
|
$this->stdClass = $stdClass;
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$someService = $this->stdClass;
|
||||||
|
$someResult = $this->stdClass->callMe();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
|
class ClassWithNamedService extends Controller
|
||||||
|
{
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$this->get('some.class')->render();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
|
class ClassWithNamedService implements ContainerAwareInterface
|
||||||
|
{
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$someService = $this->get('some.class');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
|
class ClassWithNamedService implements ContainerAwareInterface
|
||||||
|
{
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$someService = $this->get('some.class');
|
||||||
|
$someResult = $this->get('some.class')->callMe();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user