[Transform] Move HelperFunctionToConstructorInjectionRector to Transform package (#4074)

Co-authored-by: rector-bot <tomas@getrector.org>
This commit is contained in:
Tomas Votruba 2020-08-30 18:55:16 +02:00 committed by GitHub
parent e698e83687
commit 989c3f4168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 466 additions and 529 deletions

View File

@ -1,17 +0,0 @@
<?php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::AUTO_IMPORT_NAMES, false);
$parameters->set(Option::SETS, [SetList::DEAD_CODE]);
$parameters->set(Option::EXCLUDE_PATHS, ['orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php', 'orm/lib/Doctrine/ORM/Query/SqlWalker.php']);
};

View File

@ -19,7 +19,7 @@
"migrify/php-config-printer": "^0.3.31",
"nette/robot-loader": "^3.2",
"nette/utils": "^3.1",
"nikic/php-parser": "^4.9",
"nikic/php-parser": "4.9.0",
"ondram/ci-detector": "^3.4",
"phpstan/phpdoc-parser": "^0.4.9",
"phpstan/phpstan": "^0.12.38",
@ -48,7 +48,7 @@
"nette/application": "^3.0",
"nette/forms": "^3.0",
"ocramius/package-versions": "^1.4|^1.5",
"phpunit/phpunit": "^8.5|^9.0",
"phpunit/phpunit": "^8.5|^9.2",
"psr/event-dispatcher": "^1.0",
"slam/phpstan-extensions": "^5.0",
"symplify/changelog-linker": "^8.2.11",

View File

@ -3,21 +3,66 @@
declare(strict_types=1);
use Rector\Generic\Rector\FuncCall\FuncCallToNewRector;
use Rector\Laravel\Rector\FuncCall\HelperFunctionToConstructorInjectionRector;
use Rector\Laravel\Rector\StaticCall\FacadeStaticCallToConstructorInjectionRector;
use Rector\Laravel\Rector\StaticCall\RequestStaticValidateToInjectRector;
use function Rector\SymfonyPhpConfig\inline_value_objects;
use Rector\Transform\Rector\FuncCall\ArgumentFuncCallToMethodCallRector;
use Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall;
use Rector\Transform\ValueObject\ArrayFuncCallToMethodCall;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->import(__DIR__ . '/laravel-array-str-functions-to-static-call.php');
$services = $containerConfigurator->services();
$services->set(FacadeStaticCallToConstructorInjectionRector::class);
$services->set(RequestStaticValidateToInjectRector::class);
$services->set(HelperFunctionToConstructorInjectionRector::class);
// @see https://github.com/laravel/framework/blob/78828bc779e410e03cc6465f002b834eadf160d2/src/Illuminate/Foundation/helpers.php#L959
// @see https://gist.github.com/barryvdh/bb6ffc5d11e0a75dba67
$services->set(ArgumentFuncCallToMethodCallRector::class)
->call('configure', [[
ArgumentFuncCallToMethodCallRector::FUNCTIONS_TO_METHOD_CALLS => inline_value_objects([
new ArgumentFuncCallToMethodCall('auth', 'Illuminate\Contracts\Auth\Guard'),
new ArgumentFuncCallToMethodCall('policy', 'Illuminate\Contracts\Auth\Access\Gate', 'getPolicyFor'),
new ArgumentFuncCallToMethodCall('cookie', 'Illuminate\Contracts\Cookie\Factory', 'make'),
// router
new ArgumentFuncCallToMethodCall('put', 'Illuminate\Routing\Router', 'put'),
new ArgumentFuncCallToMethodCall('get', 'Illuminate\Routing\Router', 'get'),
new ArgumentFuncCallToMethodCall('post', 'Illuminate\Routing\Router', 'post'),
new ArgumentFuncCallToMethodCall('patch', 'Illuminate\Routing\Router', 'patch'),
new ArgumentFuncCallToMethodCall('delete', 'Illuminate\Routing\Router', 'delete'),
new ArgumentFuncCallToMethodCall('resource', 'Illuminate\Routing\Router', 'resource'),
new ArgumentFuncCallToMethodCall(
'response',
'Illuminate\Contracts\Routing\ResponseFactory',
'make'
),
new ArgumentFuncCallToMethodCall('info', 'Illuminate\Log\Writer', 'info'),
new ArgumentFuncCallToMethodCall('view', 'Illuminate\Contracts\View\Factory', 'make'),
new ArgumentFuncCallToMethodCall('bcrypt', 'Illuminate\Hashing\BcryptHasher', 'make'),
new ArgumentFuncCallToMethodCall('redirect', 'Illuminate\Routing\Redirector', 'back'),
new ArgumentFuncCallToMethodCall('broadcast', 'Illuminate\Contracts\Broadcasting\Factory', 'event'),
new ArgumentFuncCallToMethodCall('event', 'Illuminate\Events\Dispatcher', 'fire'),
new ArgumentFuncCallToMethodCall('dispatch', 'Illuminate\Events\Dispatcher', 'dispatch'),
new ArgumentFuncCallToMethodCall('route', 'Illuminate\Routing\UrlGenerator', 'route'),
new ArgumentFuncCallToMethodCall('asset', 'Illuminate\Routing\UrlGenerator', 'asset'),
new ArgumentFuncCallToMethodCall('url', 'Illuminate\Contracts\Routing\UrlGenerator', 'to'),
new ArgumentFuncCallToMethodCall('action', 'Illuminate\Routing\UrlGenerator', 'action'),
new ArgumentFuncCallToMethodCall('trans', 'Illuminate\Translation\Translator', 'trans'),
new ArgumentFuncCallToMethodCall(
'trans_choice',
'Illuminate\Translation\Translator',
'transChoice'
),
new ArgumentFuncCallToMethodCall('logger', 'Illuminate\Log\Writer', 'debug'),
new ArgumentFuncCallToMethodCall('back', 'Illuminate\Routing\Redirector', 'back', 'back'),
]),
ArgumentFuncCallToMethodCallRector::ARRAY_FUNCTIONS_TO_METHOD_CALLS => inline_value_objects([
new ArrayFuncCallToMethodCall('config', 'Illuminate\Contracts\Config\Repository', 'set', 'get'),
new ArrayFuncCallToMethodCall('session', 'Illuminate\Session\SessionManager', 'put', 'get'),
]),
]]);
$services->set(FuncCallToNewRector::class)
->call('configure', [[

View File

@ -23,7 +23,7 @@
- [Guzzle](#guzzle) (1)
- [Injection](#injection) (1)
- [JMS](#jms) (2)
- [Laravel](#laravel) (6)
- [Laravel](#laravel) (5)
- [Legacy](#legacy) (4)
- [MagicDisclosure](#magicdisclosure) (8)
- [MockeryToProphecy](#mockerytoprophecy) (2)
@ -70,7 +70,7 @@
- [SymfonyCodeQuality](#symfonycodequality) (1)
- [SymfonyPHPUnit](#symfonyphpunit) (1)
- [SymfonyPhpConfig](#symfonyphpconfig) (2)
- [Transform](#transform) (4)
- [Transform](#transform) (5)
- [Twig](#twig) (1)
- [TypeDeclaration](#typedeclaration) (9)
@ -6328,38 +6328,6 @@ Move Illuminate\Support\Facades\* static calls to constructor injection
<br><br>
### `HelperFunctionToConstructorInjectionRector`
- class: [`Rector\Laravel\Rector\FuncCall\HelperFunctionToConstructorInjectionRector`](/../master/rules/laravel/src/Rector/FuncCall/HelperFunctionToConstructorInjectionRector.php)
- [test fixtures](/../master/rules/laravel/tests/Rector/FuncCall/HelperFunctionToConstructorInjectionRector/Fixture)
Move help facade-like function calls to constructor injection
```diff
class SomeController
{
+ /**
+ * @var \Illuminate\Contracts\View\Factory
+ */
+ private $viewFactory;
+
+ public function __construct(\Illuminate\Contracts\View\Factory $viewFactory)
+ {
+ $this->viewFactory = $viewFactory;
+ }
+
public function action()
{
- $template = view('template.blade');
- $viewFactory = view();
+ $template = $this->viewFactory->make('template.blade');
+ $viewFactory = $this->viewFactory;
}
}
```
<br><br>
### `InlineValidationRulesToArrayDefinitionRector`
- class: [`Rector\Laravel\Rector\ArrayItem\InlineValidationRulesToArrayDefinitionRector`](/../master/rules/laravel/src/Rector/ArrayItem/InlineValidationRulesToArrayDefinitionRector.php)
@ -14161,9 +14129,59 @@ return function (ContainerConfigurator $containerConfigurator) : void {
## Transform
### `ArgumentFuncCallToMethodCallRector`
- class: [`Rector\Transform\Rector\FuncCall\ArgumentFuncCallToMethodCallRector`](/../master/rules/transform/src/Rector/FuncCall/ArgumentFuncCallToMethodCallRector.php)
Move help facade-like function calls to constructor injection
```php
<?php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Rector\Transform\Rector\FuncCall\ArgumentFuncCallToMethodCallRector;
return function (ContainerConfigurator $containerConfigurator) : void {
$services = $containerConfigurator->services();
$services->set(ArgumentFuncCallToMethodCallRector::class)
->call('configure', [[
ArgumentFuncCallToMethodCallRector::FUNCTIONS_TO_METHOD_CALLS => [
\Rector\SymfonyPhpConfig\inline_value_object(new Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall('view', 'Illuminate\Contracts\View\Factory', null, 'make'))]
]]);
};
```
```diff
class SomeController
{
+ /**
+ * @var \Illuminate\Contracts\View\Factory
+ */
+ private $viewFactory;
+
+ public function __construct(\Illuminate\Contracts\View\Factory $viewFactory)
+ {
+ $this->viewFactory = $viewFactory;
+ }
+
public function action()
{
- $template = view('template.blade');
- $viewFactory = view();
+ $template = $this->viewFactory->make('template.blade');
+ $viewFactory = $this->viewFactory;
}
}
```
<br><br>
### `MethodCallToAnotherMethodCallWithArgumentsRector`
- class: [`Rector\Transform\Rector\MethodCall\MethodCallToAnotherMethodCallWithArgumentsRector`](/../master/rules/transform/src/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector.php)
- [test fixtures](/../master/rules/transform/tests/Rector/MethodCall/MethodCallToAnotherMethodCallWithArgumentsRector/Fixture)
Turns old method call with specific types to new one with arguments

View File

@ -13,6 +13,7 @@ use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Generic\Rector\AbstractToMethodCallRector;
use Rector\Generic\ValueObject\FuncNameToMethodCallName;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Webmozart\Assert\Assert;
/**
* @see \Rector\Generic\Tests\Rector\FuncCall\FuncCallToMethodCallRector\FuncCallToMethodCallRectorTest
@ -117,6 +118,8 @@ CODE_SAMPLE
public function configure(array $configuration): void
{
$this->funcNameToMethodCallNames = $configuration[self::FUNC_CALL_TO_CLASS_METHOD_CALL] ?? [];
$funcCallsToClassMethodCalls = $configuration[self::FUNC_CALL_TO_CLASS_METHOD_CALL] ?? [];
Assert::allIsInstanceOf($funcCallsToClassMethodCalls, FuncNameToMethodCallName::class);
$this->funcNameToMethodCallNames = $funcCallsToClassMethodCalls;
}
}

View File

@ -1,204 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Laravel;
use Rector\Laravel\ValueObject\ArrayFunctionToMethodCall;
use Rector\Laravel\ValueObject\FunctionToMethodCall;
final class FunctionToServiceMap
{
/**
* @var string
*/
private const MAKE = 'make';
/**
* @var string
*/
private const PUT = 'put';
/**
* @var string
*/
private const ROUTER = 'router';
/**
* @var string
*/
private const GET = 'get';
/**
* @var string
*/
private const BACK = 'back';
/**
* @var string
*/
private const URL_GENERATOR = 'urlGenerator';
/**
* @var FunctionToMethodCall[]
*/
private $functionToMethodCalls = [];
/**
* @var ArrayFunctionToMethodCall[]
*/
private $arrayFunctionToMethodCalls = [];
public function __construct()
{
$this->initFunctionToMethodCalls();
$this->initArrayFunctionToMethodCalls();
}
/**
* @return FunctionToMethodCall|ArrayFunctionToMethodCall|null
*/
public function findByFunction(string $functionName): ?object
{
foreach ($this->functionToMethodCalls as $functionToMethodCall) {
if ($functionToMethodCall->getFunction() !== $functionName) {
continue;
}
return $functionToMethodCall;
}
foreach ($this->arrayFunctionToMethodCalls as $arrayFunctionToMethodCall) {
if ($arrayFunctionToMethodCall->getFunction() !== $functionName) {
continue;
}
return $arrayFunctionToMethodCall;
}
return null;
}
private function initFunctionToMethodCalls(): void
{
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'auth', 'Illuminate\Contracts\Auth\Guard', 'guard'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'policy', 'Illuminate\Contracts\Auth\Access\Gate', 'policy', 'getPolicyFor'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'cookie', 'Illuminate\Contracts\Cookie\Factory', 'cookieFactory', self::MAKE
);
// router
$this->functionToMethodCalls[] = new FunctionToMethodCall(
self::PUT, 'Illuminate\Routing\Router', self::ROUTER, self::PUT
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
self::GET, 'Illuminate\Routing\Router', self::ROUTER, self::GET
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'post', 'Illuminate\Routing\Router', self::ROUTER, 'post'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'patch', 'Illuminate\Routing\Router', self::ROUTER, 'patch'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'delete', 'Illuminate\Routing\Router', self::ROUTER, 'delete'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'resource', 'Illuminate\Routing\Router', self::ROUTER, 'resource'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'response', 'Illuminate\Contracts\Routing\ResponseFactory', 'responseFactory', self::MAKE
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'info', 'Illuminate\Log\Writer', 'logWriter', 'info'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'view', 'Illuminate\Contracts\View\Factory', 'viewFactory', self::MAKE
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'bcrypt', 'Illuminate\Hashing\BcryptHasher', 'bcryptHasher', self::MAKE
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'redirect', 'Illuminate\Routing\Redirector', 'redirector', self::BACK
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'broadcast', 'Illuminate\Contracts\Broadcasting\Factory', 'broadcastFactory', 'event'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'event', 'Illuminate\Events\Dispatcher', 'eventDispatcher', 'fire'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'dispatch', 'Illuminate\Events\Dispatcher', 'eventDispatcher', 'dispatch'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'route', 'Illuminate\Routing\UrlGenerator', self::URL_GENERATOR, 'route'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'asset', 'Illuminate\Routing\UrlGenerator', self::URL_GENERATOR, 'asset'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'url', 'Illuminate\Contracts\Routing\UrlGenerator', self::URL_GENERATOR, 'to'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'action', 'Illuminate\Routing\UrlGenerator', self::URL_GENERATOR, 'action'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'trans', 'Illuminate\Translation\Translator', 'translator', 'trans'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'trans_choice', 'Illuminate\Translation\Translator', 'translator', 'transChoice'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
'logger', 'Illuminate\Log\Writer', 'logWriter', 'debug'
);
$this->functionToMethodCalls[] = new FunctionToMethodCall(
self::BACK, 'Illuminate\Routing\Redirector', 'redirector', self::BACK, self::BACK
);
}
private function initArrayFunctionToMethodCalls(): void
{
$this->arrayFunctionToMethodCalls[] = new ArrayFunctionToMethodCall(
'config',
'Illuminate\Contracts\Config\Repository',
'configRepository',
'set',
self::GET
);
$this->arrayFunctionToMethodCalls[] = new ArrayFunctionToMethodCall(
'session',
'Illuminate\Session\SessionManager',
'sessionManager',
self::PUT,
self::GET
);
}
}

View File

@ -1,183 +0,0 @@
<?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\Expr\PropertyFetch;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Laravel\FunctionToServiceMap;
use Rector\Laravel\ValueObject\ArrayFunctionToMethodCall;
use Rector\Laravel\ValueObject\FunctionToMethodCall;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
/**
* @see https://github.com/laravel/framework/blob/78828bc779e410e03cc6465f002b834eadf160d2/src/Illuminate/Foundation/helpers.php#L959
* @see https://gist.github.com/barryvdh/bb6ffc5d11e0a75dba67
*
* @see \Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\HelperFunctionToConstructorInjectionRectorTest
*/
final class HelperFunctionToConstructorInjectionRector extends AbstractRector
{
/**
* @var FunctionToServiceMap
*/
private $functionToServiceMap;
public function __construct(FunctionToServiceMap $functionToServiceMap)
{
$this->functionToServiceMap = $functionToServiceMap;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Move help facade-like function calls to constructor injection', [
new CodeSample(
<<<'PHP'
class SomeController
{
public function action()
{
$template = view('template.blade');
$viewFactory = view();
}
}
PHP
,
<<<'PHP'
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;
}
}
PHP
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}
/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkipFuncCall($node)) {
return null;
}
/** @var Class_ $classLike */
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
$functionName = $this->getName($node);
if ($functionName === null) {
return null;
}
$functionChange = $this->functionToServiceMap->findByFunction($functionName);
if ($functionChange === null) {
return null;
}
$fullyQualifiedObjectType = new FullyQualifiedObjectType($functionChange->getClass());
$this->addConstructorDependencyToClass($classLike, $fullyQualifiedObjectType, $functionChange->getProperty());
$propertyFetchNode = $this->createPropertyFetch('this', $functionChange->getProperty());
if (count($node->args) === 0) {
if ($functionChange instanceof FunctionToMethodCall && $functionChange->getMethodIfNoArgs()) {
return new MethodCall($propertyFetchNode, $functionChange->getMethodIfNoArgs());
}
return $propertyFetchNode;
}
if ($this->isFunctionToMethodCallWithArgs($node, $functionChange)) {
/** @var FunctionToMethodCall $functionChange */
return new MethodCall($propertyFetchNode, $functionChange->getMethodIfArgs(), $node->args);
}
if ($functionChange instanceof ArrayFunctionToMethodCall) {
return $this->createMethodCallArrayFunctionToMethodCall($node, $functionChange, $propertyFetchNode);
}
throw new ShouldNotHappenException();
}
private function shouldSkipFuncCall(FuncCall $funcCall): bool
{
// we can inject only in class context
$classLike = $funcCall->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return true;
}
/** @var ClassMethod|null $classMethod */
$classMethod = $funcCall->getAttribute(AttributeKey::METHOD_NODE);
if ($classMethod === null) {
return true;
}
return $classMethod->isStatic();
}
/**
* @param FunctionToMethodCall|ArrayFunctionToMethodCall $functionChange
*/
private function isFunctionToMethodCallWithArgs(Node $node, $functionChange): bool
{
if (! $functionChange instanceof FunctionToMethodCall) {
return false;
}
if ($functionChange->getMethodIfArgs() === null) {
return false;
}
return count($node->args) >= 1;
}
private function createMethodCallArrayFunctionToMethodCall(
FuncCall $funcCall,
ArrayFunctionToMethodCall $arrayFunctionToMethodCall,
PropertyFetch $propertyFetch
): ?MethodCall {
if ($arrayFunctionToMethodCall->getArrayMethod() && $this->isArrayType($funcCall->args[0]->value)) {
return new MethodCall($propertyFetch, $arrayFunctionToMethodCall->getArrayMethod(), $funcCall->args);
}
if ($arrayFunctionToMethodCall->getNonArrayMethod() && ! $this->isArrayType($funcCall->args[0]->value)) {
return new MethodCall($propertyFetch, $arrayFunctionToMethodCall->getNonArrayMethod(), $funcCall->args);
}
return null;
}
}

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Laravel\Rector\FuncCall\HelperFunctionToConstructorInjectionRector;
use Symplify\SmartFileSystem\SmartFileInfo;
final class HelperFunctionToConstructorInjectionRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return HelperFunctionToConstructorInjectionRector::class;
}
}

View File

@ -80,7 +80,8 @@ final class PropertyNaming
$shortClassName = Strings::replace($shortClassName, '#_#', '');
$shortClassName = $this->normalizeUpperCase($shortClassName);
return lcfirst($shortClassName);
// prolong too short generic names with one namespace up
return $this->prolongIfTooShort($shortClassName, $className);
}
/**
@ -93,7 +94,8 @@ final class PropertyNaming
$shortName = $this->fqnToShortName($className);
$shortName = $this->removeInterfaceSuffixPrefix($className, $shortName);
return lcfirst($shortName);
// prolong too short generic names with one namespace up
return $this->prolongIfTooShort($shortName, $className);
}
/**
@ -158,6 +160,20 @@ final class PropertyNaming
return $shortClassName;
}
private function prolongIfTooShort(string $shortClassName, string $className): string
{
if (in_array($shortClassName, ['Factory', 'Repository'], true)) {
/** @var string $namespaceAbove */
$namespaceAbove = Strings::after($className, '\\', -2);
/** @var string $namespaceAbove */
$namespaceAbove = Strings::before($namespaceAbove, '\\');
return lcfirst($namespaceAbove) . $shortClassName;
}
return lcfirst($shortClassName);
}
/**
* @param ObjectType|string $objectType
*/

View File

@ -0,0 +1,263 @@
<?php
declare(strict_types=1);
namespace Rector\Transform\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall;
use Rector\Transform\ValueObject\ArrayFuncCallToMethodCall;
use Webmozart\Assert\Assert;
/**
* @see \Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\ArgumentFuncCallToMethodCallRectorTest
*/
final class ArgumentFuncCallToMethodCallRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var string
*/
public const FUNCTIONS_TO_METHOD_CALLS = 'functions_to_method_calls';
/**
* @var string
*/
public const ARRAY_FUNCTIONS_TO_METHOD_CALLS = 'array_functions_to_method_calls';
/**
* @var ArgumentFuncCallToMethodCall[]
*/
private $argumentFuncCallToMethodCalls = [];
/**
* @var ArrayFuncCallToMethodCall[]
*/
private $arrayFunctionsToMethodCalls = [];
/**
* @var PropertyNaming
*/
private $propertyNaming;
public function __construct(PropertyNaming $propertyNaming)
{
$this->propertyNaming = $propertyNaming;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Move help facade-like function calls to constructor injection', [
new ConfiguredCodeSample(
<<<'PHP'
class SomeController
{
public function action()
{
$template = view('template.blade');
$viewFactory = view();
}
}
PHP
,
<<<'PHP'
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;
}
}
PHP
,
[
self::FUNCTIONS_TO_METHOD_CALLS => [
new ArgumentFuncCallToMethodCall('view', 'Illuminate\Contracts\View\Factory', 'make'),
],
]
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}
/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkipFuncCall($node)) {
return null;
}
/** @var Class_ $classLike */
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
foreach ($this->argumentFuncCallToMethodCalls as $functionToMethodCall) {
if (! $this->isName($node, $functionToMethodCall->getFunction())) {
continue;
}
return $this->refactorFuncCallToMethodCall($functionToMethodCall, $classLike, $node);
}
foreach ($this->arrayFunctionsToMethodCalls as $arrayFunctionsToMethodCall) {
if (! $this->isName($node, $arrayFunctionsToMethodCall->getFunction())) {
continue;
}
return $this->refactorArrayFunctionToMethodCall($arrayFunctionsToMethodCall, $node, $classLike);
}
return null;
}
/**
* @param mixed[] $configuration
*/
public function configure(array $configuration): void
{
$functionToMethodCalls = $configuration[self::FUNCTIONS_TO_METHOD_CALLS] ?? [];
Assert::allIsInstanceOf($functionToMethodCalls, ArgumentFuncCallToMethodCall::class);
$this->argumentFuncCallToMethodCalls = $functionToMethodCalls;
$arrayFunctionsToMethodCalls = $configuration[self::ARRAY_FUNCTIONS_TO_METHOD_CALLS] ?? [];
Assert::allIsInstanceOf($arrayFunctionsToMethodCalls, ArrayFuncCallToMethodCall::class);
$this->arrayFunctionsToMethodCalls = $arrayFunctionsToMethodCalls;
}
private function shouldSkipFuncCall(FuncCall $funcCall): bool
{
// we can inject only in injectable class method context
/** @var ClassMethod|null $classMethod */
$classMethod = $funcCall->getAttribute(AttributeKey::METHOD_NODE);
if ($classMethod === null) {
return true;
}
return $classMethod->isStatic();
}
/**
* @return PropertyFetch|MethodCall
*/
private function refactorFuncCallToMethodCall(
ArgumentFuncCallToMethodCall $argumentFuncCallToMethodCall,
Class_ $class,
FuncCall $funcCall
): ?Node {
$fullyQualifiedObjectType = new FullyQualifiedObjectType($argumentFuncCallToMethodCall->getClass());
$propertyName = $this->propertyNaming->getExpectedNameFromType($fullyQualifiedObjectType);
if ($propertyName === null) {
throw new ShouldNotHappenException();
}
$this->addConstructorDependencyToClass($class, $fullyQualifiedObjectType, $propertyName);
$propertyFetchNode = $this->createPropertyFetch('this', $propertyName);
if (count($funcCall->args) === 0) {
if ($argumentFuncCallToMethodCall->getMethodIfNoArgs()) {
return new MethodCall($propertyFetchNode, $argumentFuncCallToMethodCall->getMethodIfNoArgs());
}
return $propertyFetchNode;
}
if ($this->isFunctionToMethodCallWithArgs($funcCall, $argumentFuncCallToMethodCall)) {
return new MethodCall(
$propertyFetchNode,
$argumentFuncCallToMethodCall->getMethodIfArgs(),
$funcCall->args
);
}
return null;
}
/**
* @return PropertyFetch|MethodCall|null
*/
private function refactorArrayFunctionToMethodCall(
ArrayFuncCallToMethodCall $arrayFuncCallToMethodCall,
FuncCall $funcCall,
Class_ $class
): ?Node {
$propertyName = $this->propertyNaming->fqnToVariableName($arrayFuncCallToMethodCall->getClass());
$propertyFetch = $this->createPropertyFetch('this', $propertyName);
$fullyQualifiedObjectType = new FullyQualifiedObjectType($arrayFuncCallToMethodCall->getClass());
$this->addConstructorDependencyToClass($class, $fullyQualifiedObjectType, $propertyName);
return $this->createMethodCallArrayFunctionToMethodCall(
$funcCall,
$arrayFuncCallToMethodCall,
$propertyFetch
);
}
private function isFunctionToMethodCallWithArgs(
FuncCall $funcCall,
ArgumentFuncCallToMethodCall $argumentFuncCallToMethodCall
): bool {
if ($argumentFuncCallToMethodCall->getMethodIfArgs() === null) {
return false;
}
return count($funcCall->args) >= 1;
}
/**
* @return PropertyFetch|MethodCall|null
*/
private function createMethodCallArrayFunctionToMethodCall(
FuncCall $funcCall,
ArrayFuncCallToMethodCall $arrayFuncCallToMethodCall,
PropertyFetch $propertyFetch
): ?Node {
if (count($funcCall->args) === 0) {
return $propertyFetch;
}
if ($arrayFuncCallToMethodCall->getArrayMethod() && $this->isArrayType($funcCall->args[0]->value)) {
return new MethodCall($propertyFetch, $arrayFuncCallToMethodCall->getArrayMethod(), $funcCall->args);
}
if ($arrayFuncCallToMethodCall->getNonArrayMethod() && ! $this->isArrayType($funcCall->args[0]->value)) {
return new MethodCall($propertyFetch, $arrayFuncCallToMethodCall->getNonArrayMethod(), $funcCall->args);
}
return null;
}
}

View File

@ -2,9 +2,9 @@
declare(strict_types=1);
namespace Rector\Laravel\ValueObject;
namespace Rector\Transform\ValueObject;
final class FunctionToMethodCall
final class ArgumentFuncCallToMethodCall
{
/**
* @var string
@ -16,11 +16,6 @@ final class FunctionToMethodCall
*/
private $class;
/**
* @var string
*/
private $property;
/**
* @var string|null
*/
@ -32,11 +27,10 @@ final class FunctionToMethodCall
private $methodIfArgs;
public function __construct(
string $function, string $class, string $property, ?string $methodIfArgs = null, ?string $methodIfNoArgs = null
string $function, string $class, ?string $methodIfArgs = null, ?string $methodIfNoArgs = null
) {
$this->function = $function;
$this->class = $class;
$this->property = $property;
$this->methodIfArgs = $methodIfArgs;
$this->methodIfNoArgs = $methodIfNoArgs;
}
@ -51,11 +45,6 @@ final class FunctionToMethodCall
return $this->class;
}
public function getProperty(): string
{
return $this->property;
}
public function getMethodIfNoArgs(): ?string
{
return $this->methodIfNoArgs;

View File

@ -2,9 +2,9 @@
declare(strict_types=1);
namespace Rector\Laravel\ValueObject;
namespace Rector\Transform\ValueObject;
final class ArrayFunctionToMethodCall
final class ArrayFuncCallToMethodCall
{
/**
* @var string
@ -16,11 +16,6 @@ final class ArrayFunctionToMethodCall
*/
private $class;
/**
* @var string
*/
private $property;
/**
* @var string
*/
@ -31,16 +26,10 @@ final class ArrayFunctionToMethodCall
*/
private $nonArrayMethod;
public function __construct(
string $function,
string $class,
string $property,
string $arrayMethod,
string $nonArrayMethod
) {
public function __construct(string $function, string $class, string $arrayMethod, string $nonArrayMethod)
{
$this->function = $function;
$this->class = $class;
$this->property = $property;
$this->arrayMethod = $arrayMethod;
$this->nonArrayMethod = $nonArrayMethod;
}
@ -55,11 +44,6 @@ final class ArrayFunctionToMethodCall
return $this->class;
}
public function getProperty(): string
{
return $this->property;
}
public function getArrayMethod(): string
{
return $this->arrayMethod;

View File

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Transform\Rector\FuncCall\ArgumentFuncCallToMethodCallRector;
use Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall;
use Rector\Transform\ValueObject\ArrayFuncCallToMethodCall;
use Symplify\SmartFileSystem\SmartFileInfo;
final class ArgumentFuncCallToMethodCallRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorsWithConfiguration(): array
{
return [
ArgumentFuncCallToMethodCallRector::class => [
ArgumentFuncCallToMethodCallRector::FUNCTIONS_TO_METHOD_CALLS => [
new ArgumentFuncCallToMethodCall('view', 'Illuminate\Contracts\View\Factory', 'make'),
new ArgumentFuncCallToMethodCall('route', 'Illuminate\Routing\UrlGenerator', 'route'),
new ArgumentFuncCallToMethodCall('back', 'Illuminate\Routing\Redirector', 'back', 'back'),
new ArgumentFuncCallToMethodCall(
'broadcast',
'Illuminate\Contracts\Broadcasting\Factory',
'event'
),
],
ArgumentFuncCallToMethodCallRector::ARRAY_FUNCTIONS_TO_METHOD_CALLS => [
new ArrayFuncCallToMethodCall('config', 'Illuminate\Contracts\Config\Repository', 'set', 'get'),
new ArrayFuncCallToMethodCall('session', 'Illuminate\Session\SessionManager', 'put', 'get'),
],
],
];
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeBackController
{
@ -19,7 +19,7 @@ class SomeBackController
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeBackController
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeBroadcastController
{
@ -14,18 +14,18 @@ class SomeBroadcastController
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeBroadcastController
{
private \Illuminate\Contracts\Broadcasting\Factory $broadcastFactory;
public function __construct(\Illuminate\Contracts\Broadcasting\Factory $broadcastFactory)
private \Illuminate\Contracts\Broadcasting\Factory $broadcastingFactory;
public function __construct(\Illuminate\Contracts\Broadcasting\Factory $broadcastingFactory)
{
$this->broadcastFactory = $broadcastFactory;
$this->broadcastingFactory = $broadcastingFactory;
}
public function action()
{
return $this->broadcastFactory->event('template.blade');
return $this->broadcastingFactory->event('template.blade');
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeConfigController
{
@ -19,7 +19,7 @@ class SomeConfigController
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeConfigController
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeRouteController
{
@ -14,7 +14,7 @@ class SomeRouteController
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeRouteController
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeSessionController
{
@ -16,7 +16,7 @@ class SomeSessionController
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeSessionController
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SkipStaticMethod
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeViewController
{
@ -15,7 +15,7 @@ class SomeViewController
-----
<?php
namespace Rector\Laravel\Tests\Rector\FuncCall\HelperFunctionToConstructorInjectionRector\Fixture;
namespace Rector\Transform\Tests\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\Fixture;
class SomeViewController
{

View File

@ -6,6 +6,7 @@ namespace Rector\Core\DependencyInjection;
use Psr\Container\ContainerInterface;
use Rector\Core\HttpKernel\RectorKernel;
use Rector\Core\Stubs\StubLoader;
use Symplify\PackageBuilder\Console\Input\InputDetector;
use Symplify\SmartFileSystem\SmartFileInfo;
@ -27,6 +28,9 @@ final class RectorContainerFactory
$rectorKernel->setConfigs($configFilePaths);
}
$stubLoader = new StubLoader();
$stubLoader->loadStubs();
$rectorKernel->boot();
return $rectorKernel->getContainer();