-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds ReplaceServiceContainerCallArgRector (#189)
* Adds ReplaceServiceContainerCallArgRector * CS Fix
- Loading branch information
Showing
9 changed files
with
340 additions
and
1 deletion.
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
156 changes: 156 additions & 0 deletions
156
src/Rector/MethodCall/ReplaceServiceContainerCallArgRector.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,156 @@ | ||
<?php | ||
|
||
namespace RectorLaravel\Rector\MethodCall; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Arg; | ||
use PhpParser\Node\Expr; | ||
use PhpParser\Node\Expr\ClassConstFetch; | ||
use PhpParser\Node\Expr\FuncCall; | ||
use PhpParser\Node\Expr\MethodCall; | ||
use PhpParser\Node\Expr\StaticCall; | ||
use PhpParser\Node\Identifier; | ||
use PhpParser\Node\Name; | ||
use PhpParser\Node\Scalar\String_; | ||
use PHPStan\Type\ObjectType; | ||
use Rector\Contract\Rector\ConfigurableRectorInterface; | ||
use Rector\Rector\AbstractRector; | ||
use RectorLaravel\ValueObject\ReplaceServiceContainerCallArg; | ||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; | ||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||
use Webmozart\Assert\Assert; | ||
|
||
/** | ||
* @see \RectorLaravel\Tests\Rector\MethodCall\ReplaceServiceContainerCallArgRector\ReplaceServiceContainerCallArgRectorTest | ||
*/ | ||
class ReplaceServiceContainerCallArgRector extends AbstractRector implements ConfigurableRectorInterface | ||
{ | ||
/** | ||
* @var ReplaceServiceContainerCallArg[] | ||
*/ | ||
private array $replaceServiceContainerCallArgs = []; | ||
|
||
public function getRuleDefinition(): RuleDefinition | ||
{ | ||
return new RuleDefinition('Changes the string or class const used for a service container make call', | ||
[new ConfiguredCodeSample( | ||
<<<'CODE_SAMPLE' | ||
app('encrypter')->encrypt('...'); | ||
\Illuminate\Support\Facades\Application::make('encrypter')->encrypt('...'); | ||
CODE_SAMPLE, | ||
<<<'CODE_SAMPLE' | ||
app(Illuminate\Contracts\Encryption\Encrypter::class)->encrypt('...'); | ||
\Illuminate\Support\Facades\Application::make(Illuminate\Contracts\Encryption\Encrypter::class)->encrypt('...'); | ||
CODE_SAMPLE, | ||
[ | ||
new ReplaceServiceContainerCallArg( | ||
'encrypter', | ||
new ClassConstFetch( | ||
new Name('Illuminate\Contracts\Encryption\Encrypter'), | ||
'class' | ||
), | ||
), | ||
] | ||
)] | ||
); | ||
} | ||
|
||
public function getNodeTypes(): array | ||
{ | ||
return [MethodCall::class, StaticCall::class, FuncCall::class]; | ||
} | ||
|
||
/** | ||
* @param MethodCall|StaticCall|FuncCall $node | ||
*/ | ||
public function refactor(Node $node): MethodCall|StaticCall|FuncCall|null | ||
{ | ||
if (! $this->validMethodCall($node) && | ||
! $this->validFuncCall($node)) { | ||
return null; | ||
} | ||
|
||
if ($node->args === [] || ! $node->args[0] instanceof Arg) { | ||
return null; | ||
} | ||
|
||
$hasChanged = false; | ||
|
||
foreach ($this->replaceServiceContainerCallArgs as $replaceServiceContainerCallArg) { | ||
if ($this->isMatchForChangeServiceContainerCallArgValue($node->args[0], $replaceServiceContainerCallArg->getOldService())) { | ||
$this->replaceCallArgValue($node->args[0], $replaceServiceContainerCallArg->getNewService()); | ||
$hasChanged = true; | ||
} | ||
} | ||
|
||
return $hasChanged ? $node : null; | ||
} | ||
|
||
public function configure(array $configuration): void | ||
{ | ||
Assert::allIsInstanceOf($configuration, ReplaceServiceContainerCallArg::class); | ||
|
||
$this->replaceServiceContainerCallArgs = $configuration; | ||
} | ||
|
||
private function isMatchForChangeServiceContainerCallArgValue(Arg $arg, ClassConstFetch|string $oldService): bool | ||
{ | ||
if ($arg->value instanceof ClassConstFetch && $oldService instanceof ClassConstFetch) { | ||
if ($arg->value->class instanceof Expr || $oldService->class instanceof Expr) { | ||
return false; | ||
} | ||
|
||
return $arg->value->class->toString() === $oldService->class->toString(); | ||
} elseif ($arg->value instanceof String_) { | ||
return $arg->value->value === $oldService; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private function replaceCallArgValue(Arg $arg, ClassConstFetch|string $newService): void | ||
{ | ||
if ($newService instanceof ClassConstFetch) { | ||
$arg->value = $newService; | ||
|
||
return; | ||
} | ||
|
||
$arg->value = new String_($newService); | ||
} | ||
|
||
private function validMethodCall(StaticCall|MethodCall|FuncCall $node): bool | ||
{ | ||
if (! $node instanceof MethodCall && ! $node instanceof StaticCall) { | ||
return false; | ||
} | ||
|
||
if (! $node->name instanceof Identifier) { | ||
return false; | ||
} | ||
|
||
if (! $this->isNames($node->name, ['make', 'get'])) { | ||
return false; | ||
} | ||
|
||
[$callObject, $class] = match (true) { | ||
$node instanceof MethodCall => [$node->var, 'Illuminate\Contracts\Container\Container'], | ||
$node instanceof StaticCall => [$node->class, 'Illuminate\Support\Facades\Application'], | ||
}; | ||
|
||
return $this->isObjectType($callObject, new ObjectType($class)); | ||
} | ||
|
||
private function validFuncCall(StaticCall|MethodCall|FuncCall $node): bool | ||
{ | ||
if (! $node instanceof FuncCall) { | ||
return false; | ||
} | ||
|
||
if (! $node->name instanceof Name) { | ||
return false; | ||
} | ||
|
||
return $this->isName($node->name, 'app'); | ||
} | ||
} |
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,24 @@ | ||
<?php | ||
|
||
namespace RectorLaravel\ValueObject; | ||
|
||
use PhpParser\Node\Expr\ClassConstFetch; | ||
|
||
final readonly class ReplaceServiceContainerCallArg | ||
{ | ||
public function __construct( | ||
private string|ClassConstFetch $oldService, | ||
private string|ClassConstFetch $newService | ||
) { | ||
} | ||
|
||
public function getOldService(): string|ClassConstFetch | ||
{ | ||
return $this->oldService; | ||
} | ||
|
||
public function getNewService(): string|ClassConstFetch | ||
{ | ||
return $this->newService; | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
tests/Rector/MethodCall/ReplaceServiceContainerCallArgRector/Fixture/fixture.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,43 @@ | ||
<?php | ||
|
||
namespace RectorLaravel\Tests\Rector\MethodCall\ReplaceServiceContainerCallArgRector\Fixture; | ||
|
||
function foo(\Illuminate\Contracts\Container\Container $app) { | ||
$app->make('encrypter')->encrypt('hello world'); | ||
} | ||
|
||
\Illuminate\Support\Facades\Application::make('encrypter')->encrypt('hello world'); | ||
|
||
app('encrypter')->encrypt('hello world'); | ||
|
||
function foo(\Illuminate\Contracts\Container\Container $app) { | ||
$app->make(\Illuminate\Contracts\Session\Session::class)->get('hello world'); | ||
} | ||
|
||
\Illuminate\Support\Facades\Application::make(\Illuminate\Contracts\Session\Session::class)->get('hello world'); | ||
|
||
app(\Illuminate\Contracts\Session\Session::class)->get('hello world'); | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace RectorLaravel\Tests\Rector\MethodCall\ReplaceServiceContainerCallArgRector\Fixture; | ||
|
||
function foo(\Illuminate\Contracts\Container\Container $app) { | ||
$app->make(\Illuminate\Contracts\Encryption\Encrypter::class)->encrypt('hello world'); | ||
} | ||
|
||
\Illuminate\Support\Facades\Application::make(\Illuminate\Contracts\Encryption\Encrypter::class)->encrypt('hello world'); | ||
|
||
app(\Illuminate\Contracts\Encryption\Encrypter::class)->encrypt('hello world'); | ||
|
||
function foo(\Illuminate\Contracts\Container\Container $app) { | ||
$app->make('session')->get('hello world'); | ||
} | ||
|
||
\Illuminate\Support\Facades\Application::make('session')->get('hello world'); | ||
|
||
app('session')->get('hello world'); | ||
|
||
?> |
13 changes: 13 additions & 0 deletions
13
...hodCall/ReplaceServiceContainerCallArgRector/Fixture/skip_non_configured_services.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,13 @@ | ||
<?php | ||
|
||
namespace RectorLaravel\Tests\Rector\MethodCall\ReplaceServiceContainerCallArgRector\Fixture; | ||
|
||
function foo(\Illuminate\Contracts\Container\Container $app) { | ||
$app->make('foobar')->encrypt('hello world'); | ||
} | ||
|
||
\Illuminate\Support\Facades\Application::make('foobar')->encrypt('hello world'); | ||
|
||
app('foobar')->encrypt('foobar'); | ||
|
||
?> |
11 changes: 11 additions & 0 deletions
11
...MethodCall/ReplaceServiceContainerCallArgRector/Fixture/skip_non_matching_classes.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,11 @@ | ||
<?php | ||
|
||
namespace RectorLaravel\Tests\Rector\MethodCall\ReplaceServiceContainerCallArgRector\Fixture; | ||
|
||
function foo ($app) { | ||
$app->make('encrypter')->encrypt('hello world'); | ||
} | ||
|
||
Application::make('encrypter')->encrypt('hello world'); | ||
|
||
?> |
11 changes: 11 additions & 0 deletions
11
...dCall/ReplaceServiceContainerCallArgRector/Fixture/skip_non_matching_method_calls.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,11 @@ | ||
<?php | ||
|
||
namespace RectorLaravel\Tests\Rector\MethodCall\ReplaceServiceContainerCallArgRector\Fixture; | ||
|
||
function foo (\Illuminate\Contracts\Container\Container $app) { | ||
$app->build('encrypter')->encrypt('hello world'); | ||
} | ||
|
||
\Illuminate\Support\Facades\Application::build('encrypter')->encrypt('hello world'); | ||
|
||
?> |
31 changes: 31 additions & 0 deletions
31
...hodCall/ReplaceServiceContainerCallArgRector/ReplaceServiceContainerCallArgRectorTest.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,31 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace RectorLaravel\Tests\Rector\MethodCall\ReplaceServiceContainerCallArgRector; | ||
|
||
use Iterator; | ||
use PHPUnit\Framework\Attributes\DataProvider; | ||
use Rector\Testing\PHPUnit\AbstractRectorTestCase; | ||
|
||
final class ReplaceServiceContainerCallArgRectorTest extends AbstractRectorTestCase | ||
{ | ||
public static function provideData(): Iterator | ||
{ | ||
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
#[DataProvider('provideData')] | ||
public function test(string $filePath): void | ||
{ | ||
$this->doTestFile($filePath); | ||
} | ||
|
||
public function provideConfigFilePath(): string | ||
{ | ||
return __DIR__ . '/config/configured_rule.php'; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
tests/Rector/MethodCall/ReplaceServiceContainerCallArgRector/config/configured_rule.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); | ||
|
||
use PhpParser\Node\Expr\ClassConstFetch; | ||
use PhpParser\Node\Name\FullyQualified; | ||
use Rector\Config\RectorConfig; | ||
use RectorLaravel\Rector\MethodCall\ReplaceServiceContainerCallArgRector; | ||
use RectorLaravel\ValueObject\ReplaceServiceContainerCallArg; | ||
|
||
return static function (RectorConfig $rectorConfig): void { | ||
$rectorConfig->import(__DIR__ . '/../../../../../config/config.php'); | ||
|
||
$rectorConfig->ruleWithConfiguration( | ||
ReplaceServiceContainerCallArgRector::class, | ||
[ | ||
new ReplaceServiceContainerCallArg( | ||
'encrypter', | ||
new ClassConstFetch( | ||
new FullyQualified('Illuminate\Contracts\Encryption\Encrypter'), | ||
'class' | ||
), | ||
), | ||
new ReplaceServiceContainerCallArg( | ||
new ClassConstFetch( | ||
new FullyQualified('Illuminate\Contracts\Session\Session'), | ||
'class' | ||
), | ||
'session', | ||
), | ||
] | ||
); | ||
}; |