[Laravel] Add FacadeStaticCallToConstructorInjectionRector

This commit is contained in:
Tomas Votruba 2019-03-03 22:29:09 +01:00
parent 6e48bf38e3
commit ca7780dfda
6 changed files with 215 additions and 1 deletions

View File

@ -0,0 +1,143 @@
<?php declare(strict_types=1);
namespace Rector\Laravel\Rector\StaticCall;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\Class_;
use Rector\Naming\PropertyNaming;
use Rector\NodeTypeResolver\Node\Attribute;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see https://medium.freecodecamp.org/moving-away-from-magic-or-why-i-dont-want-to-use-laravel-anymore-2ce098c979bd
* @see https://laravel.com/docs/5.7/facades#facades-vs-dependency-injection
*/
final class FacadeStaticCallToConstructorInjectionRector extends AbstractRector
{
/**
* @see https://laravel.com/docs/5.7/facades#facades-vs-dependency-injection
* @var string[]
*/
private $facadeToServiceMap = [
'Illuminate\Support\Facades\App' => 'Illuminate\Foundation\Application',
'Illuminate\Support\Facades\Artisan' => 'Illuminate\Contracts\Console\Kernel',
'Illuminate\Support\Facades\Auth' => 'Illuminate\Auth\AuthManager',
'Illuminate\Support\Facades\Blade' => 'Illuminate\View\Compilers\BladeCompiler',
'Illuminate\Support\Facades\Broadcast' => 'Illuminate\Contracts\Broadcasting\Factory',
'Illuminate\Support\Facades\Bus' => 'Illuminate\Contracts\Bus\Dispatcher',
'Illuminate\Support\Facades\Cache' => 'Illuminate\Cache\CacheManager',
'Illuminate\Support\Facades\Config' => 'Illuminate\Config\Repository',
'Illuminate\Support\Facades\Cookie' => 'Illuminate\Cookie\CookieJar',
'Illuminate\Support\Facades\Crypt' => 'Illuminate\Encryption\Encrypter',
'Illuminate\Support\Facades\DB' => 'Illuminate\Database\DatabaseManager',
'Illuminate\Support\Facades\Event' => 'Illuminate\Events\Dispatcher',
'Illuminate\Support\Facades\File' => 'Illuminate\Filesystem\Filesystem',
'Illuminate\Support\Facades\Gate' => 'Illuminate\Contracts\Auth\Access\Gate',
'Illuminate\Support\Facades\Hash' => 'Illuminate\Contracts\Hashing\Hasher',
'Illuminate\Support\Facades\Lang' => 'Illuminate\Translation\Translator',
'Illuminate\Support\Facades\Log' => 'Illuminate\Log\LogManager',
'Illuminate\Support\Facades\Mail' => 'Illuminate\Mail\Mailer',
'Illuminate\Support\Facades\Notification' => 'Illuminate\Notifications\ChannelManager',
'Illuminate\Support\Facades\Password' => 'Illuminate\Auth\Passwords\PasswordBrokerManager',
'Illuminate\Support\Facades\Queue' => 'Illuminate\Queue\QueueManager',
'Illuminate\Support\Facades\Redirect' => 'Illuminate\Routing\Redirector',
'Illuminate\Support\Facades\Redis' => 'Illuminate\Redis\RedisManager',
'Illuminate\Support\Facades\Request' => 'Illuminate\Http\Request',
'Illuminate\Support\Facades\Response' => 'Illuminate\Contracts\Routing\ResponseFactory',
'Illuminate\Support\Facades\Route' => 'Illuminate\Routing\Router',
'Illuminate\Support\Facades\Schema' => 'Illuminate\Database\Schema\Builder',
'Illuminate\Support\Facades\Session' => 'Illuminate\Session\SessionManager',
'Illuminate\Support\Facades\Storage' => 'Illuminate\Filesystem\FilesystemManager',
'Illuminate\Support\Facades\URL' => 'Illuminate\Routing\UrlGenerator',
'Illuminate\Support\Facades\Validator' => 'Illuminate\Validation\Factory',
'Illuminate\Support\Facades\View' => 'Illuminate\View\Factory',
];
/**
* @var PropertyNaming
*/
private $propertyNaming;
public function __construct(PropertyNaming $propertyNaming)
{
$this->propertyNaming = $propertyNaming;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Move Illuminate\Support\Facades\Response static calls to constructor injection', [
new CodeSample(
<<<'CODE_SAMPLE'
use Illuminate\Support\Facades\Response;
class ExampleController extends Controller
{
public function store()
{
return Response::view('example', ['new_example' => 123]);
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
use Illuminate\Support\Facades\Response;
class ExampleController extends Controller
{
/**
* @var \Illuminate\Contracts\Routing\ResponseFactory
*/
private $responseFactory;
public function __construct(\Illuminate\Contracts\Routing\ResponseFactory $responseFactory)
{
$this->responseFactory = $responseFactory;
}
public function store()
{
return $this->responseFactory->view('example', ['new_example' => 123]);
}
}
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [StaticCall::class];
}
/**
* @param StaticCall $node
*/
public function refactor(Node $node): ?Node
{
$classNode = $node->getAttribute(Attribute::CLASS_NODE);
if (! $classNode instanceof Class_) {
return null;
}
foreach ($this->facadeToServiceMap as $facadeClass => $serviceClass) {
if (! $this->isType($node, $facadeClass)) {
continue;
}
$propertyName = $this->propertyNaming->fqnToVariableName($serviceClass);
$this->addPropertyToClass($classNode, $serviceClass, $propertyName);
$propertyFetchNode = $this->createPropertyFetch('this', $propertyName);
return new MethodCall($propertyFetchNode, $node->name, $node->args);
}
return $node;
}
}

View File

@ -0,0 +1,19 @@
<?php declare(strict_types=1);
namespace Rector\Laravel\Tests\Rector\StaticCall\FacadeStaticCallToConstructorInjectionRector;
use Rector\Laravel\Rector\StaticCall\FacadeStaticCallToConstructorInjectionRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class FacadeStaticCallToConstructorInjectionRectorTest extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFiles([__DIR__ . '/Fixture/fixture.php.inc']);
}
protected function getRectorClass(): string
{
return FacadeStaticCallToConstructorInjectionRector::class;
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Rector\Laravel\Tests\Rector\StaticCall\FacadeStaticCallToConstructorInjectionRector\Fixture;
use Illuminate\Support\Facades\Response;
use Rector\Laravel\Tests\Rector\StaticCall\FacadeStaticCallToConstructorInjectionRector\Source\LaravelController;
class ExampleController extends LaravelController
{
public function store()
{
return Response::view('example', ['new_example' => 123]);
}
}
?>
-----
<?php
namespace Rector\Laravel\Tests\Rector\StaticCall\FacadeStaticCallToConstructorInjectionRector\Fixture;
use Illuminate\Support\Facades\Response;
use Rector\Laravel\Tests\Rector\StaticCall\FacadeStaticCallToConstructorInjectionRector\Source\LaravelController;
class ExampleController extends LaravelController
{
/**
* @var \Illuminate\Contracts\Routing\ResponseFactory
*/
private $responseFactory;
public function __construct(\Illuminate\Contracts\Routing\ResponseFactory $responseFactory)
{
$this->responseFactory = $responseFactory;
}
public function store()
{
return $this->responseFactory->view('example', ['new_example' => 123]);
}
}
?>

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace Rector\Laravel\Tests\Rector\StaticCall\FacadeStaticCallToConstructorInjectionRector\Source;
abstract class LaravelController
{
}

View File

@ -102,7 +102,9 @@ final class NodeTypeResolver
continue;
}
$types += $this->classReflectionTypesResolver->resolve($this->broker->getClass($type));
if (class_exists($type)) {
$types += $this->classReflectionTypesResolver->resolve($this->broker->getClass($type));
}
}
return $types;

View File

@ -46,6 +46,7 @@ final class DocBlockManipulator
* @var TypeAnalyzer
*/
private $typeAnalyzer;
/**
* @var AttributeAwareNodeFactory
*/