Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# All 489 Rectors Overview
# All 490 Rectors Overview

- [Projects](#projects)
- [General](#general)
Expand All @@ -22,7 +22,7 @@
- [Guzzle](#guzzle) (1)
- [JMS](#jms) (2)
- [Laravel](#laravel) (6)
- [Legacy](#legacy) (1)
- [Legacy](#legacy) (2)
- [MysqlToMysqli](#mysqltomysqli) (4)
- [Naming](#naming) (1)
- [Nette](#nette) (11)
Expand Down Expand Up @@ -4145,6 +4145,27 @@ Change singleton class to normal class that can be registered as a service

<br>

### `FunctionToStaticMethodRector`

- class: [`Rector\Legacy\Rector\Node\FunctionToStaticMethodRector`](/../master/rules/legacy/src/Rector/Node/FunctionToStaticMethodRector.php)

Change functions to static calls, so composer can autoload them

```diff
-function some_function()
+class SomeUtilsClass
{
+ public static function someFunction()
+ {
+ }
}

-some_function('lol');
+SomeUtilsClass::someFunction('lol');
```

<br>

## MysqlToMysqli

### `MysqlAssignToMysqliRector`
Expand Down
4 changes: 4 additions & 0 deletions ecs.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
services:
SlevomatCodingStandard\Sniffs\Commenting\DisallowCommentAfterCodeSniff: null
SlevomatCodingStandard\Sniffs\Whitespaces\DuplicateSpacesSniff: null

parameters:
paths:
- "bin"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ public function isFunctionUsed(string $functionName): bool
private function addMethod(ClassMethod $classMethod): void
{
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
if ($className === null) { // anonymous
// anonymous
if ($className === null) {
return;
}

Expand All @@ -192,7 +193,8 @@ private function addCall(Node $node): void
$classType = $this->resolveNodeClassTypes($node->class);
}

if ($classType instanceof MixedType) { // anonymous
// anonymous
if ($classType instanceof MixedType) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public function findByClassMethod(ClassMethod $classMethod): array
{
/** @var string|null $className */
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
if ($className === null) { // anonymous
// anonymous
if ($className === null) {
return [];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ public function decorateNodesFromFile(array $nodes, SmartFileInfo $smartFileInfo
$nodes = $nodeTraverser->traverse($nodes);

$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor($this->cloningVisitor); // needed also for format preserving printing
// needed also for format preserving printing
$nodeTraverser->addVisitor($this->cloningVisitor);
$nodeTraverser->addVisitor($this->parentAndNextNodeVisitor);
$nodeTraverser->addVisitor($this->functionMethodAndClassNodeVisitor);
$nodeTraverser->addVisitor($this->namespaceNodeVisitor);
Expand Down
2 changes: 1 addition & 1 deletion packages/node-type-resolver/src/PHPStan/TypeComparator.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private function areAliasedObjectMatchingFqnObject(Type $firstType, Type $second
}

/**
* E.g. class A extends B, class B → A[] is subtype of B[] → keep A[]
* E.g. class A extends B, class B → A[] is subtype of B[] → keep A[]
*/
private function areArrayTypeWithSingleObjectChildToParent(Type $firstType, Type $secondType): bool
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public function refactor(Node $node): ?Node
}

$onlyArrayItem = $arrayNode->items[0]->value;
if (isset($node->args[2])) { // strict
// strict
if (isset($node->args[2])) {
return new Identical($node->args[0]->value, $onlyArrayItem);
}

Expand Down
19 changes: 19 additions & 0 deletions rules/coding-style/src/Naming/ClassNaming.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
use Nette\Utils\Strings;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Function_;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Util\StaticRectorStrings;
use Rector\NodeNameResolver\NodeNameResolver;
use Symplify\SmartFileSystem\SmartFileInfo;

final class ClassNaming
{
Expand Down Expand Up @@ -45,4 +48,20 @@ public function getNamespace(string $fullyQualifiedName): ?string

return Strings::before($fullyQualifiedName, '\\', -1) ?: null;
}

public function getNameFromFileInfo(SmartFileInfo $smartFileInfo): string
{
$basename = $smartFileInfo->getBasenameWithoutSuffix();

return StaticRectorStrings::underscoreToCamelCase($basename);
}

/**
* "some_function" → "someFunction"
*/
public function createMethodNameFromFunction(Function_ $function): string
{
$functionName = (string) $function->name;
return StaticRectorStrings::underscoreToPascalCase($functionName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ private function resolveDefaultValuesFromCall(Node $node): array

/** @var string|null $className */
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
if ($className === null) { // anonymous class
// anonymous class
if ($className === null) {
return [];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ private function getTypesToMethods(): array
{
return [
'Illuminate\Support\Facades\Cache' => [
'put' => 2, // time argument position
// time argument position
'put' => 2,
'add' => 2,
],
Store::class => [
Expand Down
1 change: 1 addition & 0 deletions rules/legacy/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ services:
resource: '../src'
exclude:
- '../src/Rector/**/*Rector.php'
- '../src/ValueObject/*'
184 changes: 184 additions & 0 deletions rules/legacy/src/Rector/Node/FunctionToStaticMethodRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<?php

declare(strict_types=1);

namespace Rector\Legacy\Rector\Node;

use PhpParser\Builder\Class_ as ClassBuilder;
use PhpParser\Builder\Method;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Namespace_;
use Rector\CodingStyle\Naming\ClassNaming;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\FileSystemRector\Rector\AbstractFileSystemRector;
use Rector\Legacy\ValueObject\StaticCallPointer;
use Symplify\SmartFileSystem\SmartFileInfo;

/**
* @see \Rector\Legacy\Tests\Rector\FileSystem\FunctionToStaticMethodRector\FunctionToStaticMethodRectorTest
*/
final class FunctionToStaticMethodRector extends AbstractFileSystemRector
{
/**
* @var ClassNaming
*/
private $classNaming;

/**
* @var StaticCallPointer[]
*/
private $functionNameToClassStaticMethod = [];

public function __construct(ClassNaming $classNaming)
{
$this->classNaming = $classNaming;
}

public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Change functions to static calls, so composer can autoload them', [
new CodeSample(
<<<'PHP'
function some_function()
{
}

some_function('lol');
PHP
,
<<<'PHP'
class SomeUtilsClass
{
public static function someFunction()
{
}
}

SomeUtilsClass::someFunction('lol');
PHP

),
]);
}

public function refactor(SmartFileInfo $smartFileInfo): void
{
$nodes = $this->parseFileInfoToNodes($smartFileInfo);
$fileStmts = $this->getFileOrNamespaceStmts($nodes);

/** @var Function_[] $functions */
$functions = $this->betterNodeFinder->findInstanceOf($fileStmts, Function_::class);
if ($functions === []) {
return;
}

$shortClassName = $this->classNaming->getNameFromFileInfo($smartFileInfo);
$classBuilder = new ClassBuilder($shortClassName);
$classBuilder->makeFinal();

$className = $this->getFullyQualifiedName($nodes, $shortClassName);

foreach ($functions as $function) {
$functionName = $this->getName($function);
$methodName = $this->classNaming->createMethodNameFromFunction($function);
$this->functionNameToClassStaticMethod[$functionName] = new StaticCallPointer($className, $methodName);

$staticClassMethod = $this->createClassMethodFromFunction($methodName, $function);
$classBuilder->addStmt($staticClassMethod);

// remove after convert, we won't need it
$this->removeNode($function);
}

$class = $classBuilder->getNode();

$classFilePath = $smartFileInfo->getPath() . DIRECTORY_SEPARATOR . $shortClassName . '.php';
$nodesToPrint = $this->resolveNodesToPrint($nodes, $class);

// replace function calls with class static call

$this->traverseNodesWithCallable($nodes, function (Node $node) {
if (! $node instanceof FuncCall) {
return null;
}

$funcCallName = $this->getName($node);
$staticCallPointer = $this->functionNameToClassStaticMethod[$funcCallName] ?? null;
if ($staticCallPointer === null) {
return null;
}

$staticCall = $this->createStaticCall($staticCallPointer->getClass(), $staticCallPointer->getMethod());
$staticCall->args = $node->args;

return $staticCall;
});

// @todo decouple to PostRectorInterface, so it's covered in external files too
$this->printNewNodesToFilePath($nodesToPrint, $classFilePath);
}

/**
* @param Node[] $nodes
* @return Node[]
*/
private function resolveNodesToPrint(array $nodes, Class_ $class): array
{
/** @var Namespace_|null $namespace */
$namespace = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class);
if ($namespace !== null) {
// put class first
$namespace->stmts = array_merge([$class], $namespace->stmts);

return [$namespace];
}

return [$class];
}

/**
* @param Node[] $nodes
* @return Node[]
*/
private function getFileOrNamespaceStmts(array $nodes): array
{
/** @var Namespace_|null $namespace */
$namespace = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class);
if ($namespace === null) {
return $nodes;
}

return $namespace->stmts;
}

private function getFullyQualifiedName(array $nodes, string $shortClassName): string
{
/** @var Namespace_|null $namespace */
$namespace = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class);
if ($namespace === null) {
return $shortClassName;
}

$namespaceName = $this->getName($namespace);
if ($namespaceName === null) {
return $shortClassName;
}

return $namespaceName . '\\' . $shortClassName;
}

private function createClassMethodFromFunction(string $methodName, Function_ $function): ClassMethod
{
$methodBuilder = new Method($methodName);
$methodBuilder->makePublic();
$methodBuilder->makeStatic();
$methodBuilder->addStmts($function->stmts);

return $methodBuilder->getNode();
}
}
34 changes: 34 additions & 0 deletions rules/legacy/src/ValueObject/StaticCallPointer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Rector\Legacy\ValueObject;

final class StaticCallPointer
{
/**
* @var string
*/
private $class;

/**
* @var string
*/
private $method;

public function __construct(string $class, string $method)
{
$this->class = $class;
$this->method = $method;
}

public function getClass(): string
{
return $this->class;
}

public function getMethod(): string
{
return $this->method;
}
}
Loading