-
-
Notifications
You must be signed in to change notification settings - Fork 704
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Reconstructor] NamedServicesToConstructor init
- Loading branch information
1 parent
ad5ba53
commit e59acf0
Showing
8 changed files
with
236 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
src/Reconstructor/DependencyInjection/NamedServicesToConstructorReconstructor.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
...ructor/DependencyInjection/NamedServicesToConstructorReconstructor/Source/LocalKernel.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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'; | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
...nstructor/DependencyInjection/NamedServicesToConstructorReconstructor/Source/services.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
services: | ||
some.class: | ||
class: stdClass |
22 changes: 22 additions & 0 deletions
22
tests/Reconstructor/DependencyInjection/NamedServicesToConstructorReconstructor/Test.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...uctor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
...nstructor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php declare (strict_types=1); | ||
|
||
class ClassWitNamedService | ||
{ | ||
public function render() | ||
{ | ||
$this->get('some.class')->render(); | ||
} | ||
} |