mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-06 03:10:50 +00:00
[Reconstructor] NamedServicesToConstructor init
This commit is contained in:
parent
ad5ba530c7
commit
e59acf0d18
|
@ -11,7 +11,8 @@
|
|||
"symfony/console": "^3.3",
|
||||
"symfony/dependency-injection": "^3.3",
|
||||
"nikic/php-parser": "^3.0",
|
||||
"ocramius/code-generator-utils": "^0.4.1"
|
||||
"ocramius/code-generator-utils": "^0.4",
|
||||
"nette/utils": "^2.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.2",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Rector\Builder;
|
||||
|
||||
use Nette\Utils\Arrays;
|
||||
use PhpParser\Builder\Method;
|
||||
use PhpParser\Builder\Param;
|
||||
use PhpParser\BuilderFactory;
|
||||
|
@ -49,7 +50,7 @@ final class ConstructorMethodBuilder
|
|||
->addParam($this->createParameter($propertyType, $propertyName))
|
||||
->addStmts($assign);
|
||||
|
||||
$classNode->stmts[] = $constructorMethod->getNode();
|
||||
$this->addAsFirstMethod($classNode, $constructorMethod);
|
||||
}
|
||||
|
||||
private function createParameter(string $propertyType, string $propertyName): Param
|
||||
|
@ -69,4 +70,21 @@ final class ConstructorMethodBuilder
|
|||
$propertyName
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private function addAsFirstMethod(Class_ $classNode, Method $constructorMethod): void
|
||||
{
|
||||
foreach ($classNode->stmts as $key => $classElementNode) {
|
||||
if ($classElementNode instanceof ClassMethod) {
|
||||
Arrays::insertBefore(
|
||||
$classNode->stmts,
|
||||
$key,
|
||||
[$constructorMethod->getNode()]
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$classNode->stmts[] = $constructorMethod->getNode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Reconstructor\DependencyInjection;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Builder\ConstructorMethodBuilder;
|
||||
use Rector\Contract\Dispatcher\ReconstructorInterface;
|
||||
use Rector\Tests\Reconstructor\DependencyInjection\NamedServicesToConstructorReconstructor\Source\LocalKernel;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
final class NamedServicesToConstructorReconstructor implements ReconstructorInterface
|
||||
{
|
||||
/**
|
||||
* @var ConstructorMethodBuilder
|
||||
*/
|
||||
private $constructorMethodBuilder;
|
||||
|
||||
public function __construct(ConstructorMethodBuilder $constructorMethodBuilder)
|
||||
{
|
||||
$this->constructorMethodBuilder = $constructorMethodBuilder;
|
||||
}
|
||||
|
||||
public function isCandidate(Node $node): bool
|
||||
{
|
||||
return $node instanceof Class_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_|Node $classNode
|
||||
*/
|
||||
public function reconstruct(Node $classNode): void
|
||||
{
|
||||
foreach ($classNode->stmts as $classElementStatement) {
|
||||
// 1. Detect method
|
||||
if (! $classElementStatement instanceof ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classMethodNode = $classElementStatement;
|
||||
|
||||
foreach ($classMethodNode->stmts as $classMethodStatement) {
|
||||
// 2. Find ->get('...') call in it
|
||||
if (! $classMethodStatement instanceof MethodCall) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodCallNode = $classMethodStatement;
|
||||
// A. Find ->get('...')->someCall()
|
||||
/**
|
||||
* @todo: process also $var = $this->get('...');
|
||||
* not a MethodCall on service, but Assign/PropertyFetch
|
||||
*/
|
||||
if (! $methodCallNode->var instanceof MethodCall) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodCallNode = $methodCallNode->var;
|
||||
|
||||
// 3. Accept only "$this->get()"
|
||||
if ($methodCallNode->name !== 'get') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4. Accept only strings in "$this->get('string')"
|
||||
$argument = $methodCallNode->args[0]->value;
|
||||
if (! $methodCallNode->args[0]->value instanceof String_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var String_ $argument */
|
||||
$serviceName = $argument->value;
|
||||
|
||||
$container = $this->getContainerFromKernelClass();
|
||||
if (! $container->has($serviceName)) {
|
||||
// service name could not be found
|
||||
continue;
|
||||
}
|
||||
|
||||
$service = $container->get($serviceName);
|
||||
|
||||
// 6. Save Services
|
||||
$serviceType = get_class($service);
|
||||
$propertyName = $this->createPropertyNameFromClass($serviceType);
|
||||
$collectedServices[$propertyName] = $serviceType;
|
||||
|
||||
// 7. Replace "$this->get()" => "$this->{$propertyName}"
|
||||
// A.
|
||||
|
||||
// 7.1 Replace "$this" with "$this->propertyName"
|
||||
$methodCallNode->var = new PropertyFetch(
|
||||
new Variable('this', [
|
||||
'name' => $propertyName
|
||||
]), '' // @todo: with annotation!
|
||||
);
|
||||
|
||||
// 8. add this property to constructor
|
||||
$this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $serviceType, $propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo extract to helper service, LocalKernelProvider::get...()
|
||||
*/
|
||||
private function getContainerFromKernelClass(): ContainerInterface
|
||||
{
|
||||
/** @var Kernel $kernel */
|
||||
$kernel = new LocalKernel('dev', true);
|
||||
$kernel->boot();
|
||||
|
||||
// @todo: initialize without creating cache or log directory
|
||||
// @todo: call only loadBundles() and initializeContainer() methods
|
||||
|
||||
return $kernel->getContainer();
|
||||
}
|
||||
|
||||
private function createPropertyNameFromClass(string $serviceType): string
|
||||
{
|
||||
$serviceNameParts = explode('\\', $serviceType);
|
||||
$lastNamePart = array_pop($serviceNameParts);
|
||||
|
||||
return lcfirst($lastNamePart);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Reconstructor\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,22 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Reconstructor\DependencyInjection\NamedServicesToConstructorReconstructor;
|
||||
|
||||
use Rector\Reconstructor\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'
|
||||
);
|
||||
}
|
||||
|
||||
protected function getReconstructorClass(): string
|
||||
{
|
||||
return NamedServicesToConstructorReconstructor::class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php declare (strict_types=1);
|
||||
class ClassWitNamedService
|
||||
{
|
||||
/**
|
||||
* @var SomeClass
|
||||
*/
|
||||
private $someClass;
|
||||
public function __construct(SomeClass $someClass)
|
||||
{
|
||||
$this->someClass = $someClass;
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
$this->someClass->render();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php declare (strict_types=1);
|
||||
|
||||
class ClassWitNamedService
|
||||
{
|
||||
public function render()
|
||||
{
|
||||
$this->get('some.class')->render();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user