[Laravel] Add HelperFunctionToConstructorInjectionRector

This commit is contained in:
Tomas Votruba 2019-03-10 12:18:16 +00:00
parent 8bf7bfb12f
commit b22d8921d7
9 changed files with 461 additions and 0 deletions

View File

@ -5,6 +5,7 @@ imports:
services:
Rector\Laravel\Rector\StaticCall\FacadeStaticCallToConstructorInjectionRector: ~
Rector\Laravel\Rector\StaticCall\RequestStaticValidateToInjectRector: ~
Rector\Laravel\Rector\FuncCall\HelperFunctionToConstructorInjectionRector: ~
Rector\Rector\FuncCall\FunctionToNewRector:
collect: 'Illuminate\Support\Collection'

View File

@ -104,6 +104,7 @@ parameters:
- 'packages/ContributorTools/src/Command/DumpNodesCommand.php'
- 'packages/CodeQuality/src/Rector/Identical/SimplifyBoolIdenticalTrueRector.php'
- 'packages/BetterPhpDocParser/src/Attributes/Ast/AttributeAwareNodeFactory.php'
- 'packages/Laravel/src/Rector/FuncCall/HelperFunctionToConstructorInjectionRector.php'
# copied 3rd party logic
- 'packages/Php/src/EregToPcreTransformer.php'

View File

@ -0,0 +1,254 @@
<?php declare(strict_types=1);
namespace Rector\Laravel\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\Node\Attribute;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see https://github.com/laravel/framework/blob/78828bc779e410e03cc6465f002b834eadf160d2/src/Illuminate/Foundation/helpers.php#L959
* @see https://gist.github.com/barryvdh/bb6ffc5d11e0a75dba67
*/
final class HelperFunctionToConstructorInjectionRector extends AbstractRector
{
/**
* @var string[][]
*/
private $functionToService = [
// set/get
'config' => [
'type' => 'Illuminate\Contracts\Config\Repository',
'array_method' => 'set',
'non_array_method' => 'get',
],
'session' => [
'type' => 'Illuminate\Session\SessionManager',
'property' => 'sessionManager',
'array_method' => 'put',
'non_array_method' => 'get',
],
// methods if args/property fetch
'policy' => [
'type' => 'Illuminate\Contracts\Auth\Access\Gate',
'property' => 'policy',
'method_if_args' => 'getPolicyFor',
],
'cookie' => [
'type' => 'Illuminate\Contracts\Cookie\Factory',
'property' => 'cookieFactory',
'method_if_args' => 'make',
],
// router
'put' => [
'type' => 'Illuminate\Routing\Router',
'property' => 'router',
'method_if_args' => 'put',
],
'get' => [
'type' => 'Illuminate\Routing\Router',
'property' => 'router',
'method_if_args' => 'get',
],
'post' => [
'type' => 'Illuminate\Routing\Router',
'property' => 'router',
'method_if_args' => 'post',
],
'patch' => [
'type' => 'Illuminate\Routing\Router',
'property' => 'router',
'method_if_args' => 'patch',
],
'delete' => [
'type' => 'Illuminate\Routing\Router',
'property' => 'router',
'method_if_args' => 'delete',
],
'resource' => [
'type' => 'Illuminate\Routing\Router',
'property' => 'router',
'method_if_args' => 'resource',
],
'response' => [
'type' => 'Illuminate\Contracts\Routing\ResponseFactory',
'property' => 'responseFactory',
'method_if_args' => 'make',
],
'info' => [
'type' => 'Illuminate\Log\Writer',
'property' => 'logWriter',
'method_if_args' => 'info',
],
'view' => [
'type' => 'Illuminate\Contracts\View\Factory',
'property' => 'viewFactory',
'method_if_args' => 'make',
],
'bcrypt' => [
'type' => 'Illuminate\Hashing\BcryptHasher',
'property' => 'bcryptHasher',
'method_if_args' => 'make',
],
'redirect' => [
'type' => 'Illuminate\Routing\Redirector',
'property' => 'redirector',
'method_if_args' => 'back',
],
'back' => [
'type' => 'Illuminate\Routing\Redirector',
'property' => 'redirector',
'method_if_args' => 'back',
],
'broadcast' => [
'type' => 'Illuminate\Contracts\Broadcasting\Factory',
'property' => 'broadcastFactory',
'method_if_args' => 'event',
],
'event' => [
'type' => 'Illuminate\Events\Dispatcher',
'property' => 'eventDispatcher',
'method_if_args' => 'fire',
],
'dispatch' => [
'type' => 'Illuminate\Events\Dispatcher',
'property' => 'eventDispatcher',
'method_if_args' => 'dispatch',
],
'route' => [
'type' => 'Illuminate\Routing\UrlGenerator',
'property' => 'urlGenerator',
'method_if_args' => 'route',
],
'auth' => [
'type' => 'Illuminate\Contracts\Auth\Guard',
'property' => 'guard',
],
'asset' => [
'type' => 'Illuminate\Routing\UrlGenerator',
'property' => 'urlGenerator',
'method_if_args' => 'asset',
],
'url' => [
'type' => 'Illuminate\Contracts\Routing\UrlGenerator',
'property' => 'urlGenerator',
'method_if_args' => 'to',
],
'action' => [
'type' => 'Illuminate\Routing\UrlGenerator',
'property' => 'urlGenerator',
'method_if_args' => 'action',
],
'trans' => [
'type' => 'Illuminate\Translation\Translator',
'property' => 'translator',
'method_if_args' => 'trans',
],
'trans_choice' => [
'type' => 'Illuminate\Translation\Translator',
'property' => 'translator',
'method_if_args' => 'transChoice',
],
'logger' => [
'type' => 'Illuminate\Log\Writer',
'property' => 'logWriter',
'method_if_args' => 'debug',
],
];
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Move help facade-like function calls to constructor injection', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeController
{
public function action()
{
$template = view('template.blade');
$viewFactory = view();
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeController
{
/**
* @var \Illuminate\Contracts\View\Factory
*/
private $viewFactory;
public function __construct(\Illuminate\Contracts\View\Factory $viewFactory)
{
$this->viewFactory = $viewFactory;
}
public function action()
{
$template = $this->viewFactory->make('template.blade');
$viewFactory = $this->viewFactory;
}
}
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}
/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
// we can inject only in class context
$classNode = $node->getAttribute(Attribute::CLASS_NODE);
if (! $classNode instanceof Class_) {
return null;
}
foreach ($this->functionToService as $function => $service) {
if (! $this->isName($node, $function)) {
continue;
}
$this->addPropertyToClass($classNode, $service['type'], $service['property']);
$propertyFetchNode = $this->createPropertyFetch('this', $service['property']);
if (count($node->args) === 0) {
return $propertyFetchNode;
}
if (isset($service['method_if_args']) && count($node->args) >= 1) {
return new MethodCall($propertyFetchNode, $service['method_if_args'], $node->args);
}
if (isset($service['array_method']) && $this->isArrayType($node->args[0]->value)) {
return new MethodCall($propertyFetchNode, $service['array_method'], $node->args);
}
if (isset($service['non_array_method']) && ! $this->isArrayType($node->args[0]->value)) {
return new MethodCall($propertyFetchNode, $service['non_array_method'], $node->args);
}
throw new ShouldNotHappenException();
}
return null;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeBackController
{
public function action()
{
return back('template.blade');
}
}
?>
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeBackController
{
/**
* @var \Illuminate\Routing\UrlGenerator
*/
private $urlGenerator;
public function __construct(\Illuminate\Routing\UrlGenerator $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function action()
{
return $this->urlGenerator->route('template.blade');
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeBroadcastController
{
public function action()
{
return broadcast('template.blade');
}
}
?>
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeBroadcastController
{
/**
* @var \Illuminate\Contracts\Broadcasting\Factory
*/
private $broadcastFactory;
public function __construct(\Illuminate\Contracts\Broadcasting\Factory $broadcastFactory)
{
$this->broadcastFactory = $broadcastFactory;
}
public function action()
{
return $this->broadcastFactory->event('template.blade');
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeRouteController
{
public function action()
{
return route('template.blade');
}
}
?>
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeRouteController
{
/**
* @var \Illuminate\Routing\UrlGenerator
*/
private $urlGenerator;
public function __construct(\Illuminate\Routing\UrlGenerator $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function action()
{
return $this->urlGenerator->route('template.blade');
}
}
?>

View File

@ -0,0 +1,39 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeSessionController
{
public function action()
{
$session = session();
session(['key']);
session('key', 'value');
}
}
?>
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeSessionController
{
/**
* @var \Illuminate\Session\SessionManager
*/
private $sessionManager;
public function __construct(\Illuminate\Session\SessionManager $sessionManager)
{
$this->sessionManager = $sessionManager;
}
public function action()
{
$session = $this->sessionManager;
$this->sessionManager->put(['key']);
$this->sessionManager->get('key', 'value');
}
}
?>

View File

@ -0,0 +1,37 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeViewController
{
public function action()
{
$template = view('template.blade');
$viewFactory = view();
}
}
?>
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
class SomeViewController
{
/**
* @var \Illuminate\Contracts\View\Factory
*/
private $viewFactory;
public function __construct(\Illuminate\Contracts\View\Factory $viewFactory)
{
$this->viewFactory = $viewFactory;
}
public function action()
{
$template = $this->viewFactory->make('template.blade');
$viewFactory = $this->viewFactory;
}
}
?>

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector;
use Rector\Laravel\Rector\FuncCall\HelperFunctionToConstructorInjectionRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class HelperFunctionToConstructorInjectionRectorTest extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFiles([
__DIR__ . '/Fixture/view.php.inc',
__DIR__ . '/Fixture/broadcast.php.inc',
__DIR__ . '/Fixture/session.php.inc',
__DIR__ . '/Fixture/route.php.inc',
]);
}
protected function getRectorClass(): string
{
return HelperFunctionToConstructorInjectionRector::class;
}
}