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
5 changes: 5 additions & 0 deletions config/set/coding-style/coding-style.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ services:
Rector\CodingStyle\Rector\Class_\AddArrayDefaultToArrayPropertyRector: ~
Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector: ~
Rector\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector: ~
Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector: ~
Rector\CodingStyle\Rector\FuncCall\FunctionCallToConstantRector:
$functionsToConstants:
php_sapi_name: PHP_SAPI
pi: M_PI
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php declare(strict_types=1);

namespace Rector\CodingStyle\Rector\FuncCall;

use PhpParser\Node;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;

/**
* @see \Rector\CodingStyle\Tests\Rector\FuncCall\FunctionCallToConstantRector\FunctionCallToConstantRectorTest
*/
final class FunctionCallToConstantRector extends AbstractRector
Comment thread
TomasVotruba marked this conversation as resolved.
{
/**
* @var string[]string
*/
private $functionsToConstants;

/**
* @param string[] $functionsToConstants
*/
public function __construct(array $functionsToConstants = [])
{
$this->functionsToConstants = $functionsToConstants;
}

public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Changes use of function calls to use constants', [
new CodeSample(
<<<'EOS'
class SomeClass
{
public function run()
{
$value = php_sapi_name();
}
}
EOS
,
<<<'EOS'
class SomeClass
{
public function run()
{
$value = PHP_SAPI;
}
}
EOS
),
new CodeSample(
<<<'EOS'
class SomeClass
{
public function run()
{
$value = pi();
}
}
EOS
,
<<<'EOS'
class SomeClass
{
public function run()
{
$value = M_PI;
}
}
EOS
),
]);
}

/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}

/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
$functionName = $this->getName($node);
if (! $functionName || ! array_key_exists($functionName, $this->functionsToConstants)) {
return null;
}

return new ConstFetch(new Name($this->functionsToConstants[$functionName]));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php declare(strict_types=1);

namespace Rector\CodingStyle\Rector\FuncCall;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Greater;
use PhpParser\Node\Expr\BinaryOp\GreaterOrEqual;
use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Expr\BinaryOp\Smaller;
use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use Rector\Exception\ShouldNotHappenException;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;

/**
* @see \Rector\CodingStyle\Tests\Rector\FuncCall\FunctionCallToConstantRector\FunctionCallToConstantRectorTest
*/
final class VersionCompareFuncCallToConstantRector extends AbstractRector
{
/**
* @var string[]string
*/
private $operatorToComparison = [
'=' => Identical::class,
'==' => Identical::class,
'eq' => Identical::class,
'!=' => NotIdentical::class,
'<>' => NotIdentical::class,
'ne' => NotIdentical::class,
'>' => Greater::class,
'gt' => Greater::class,
'<' => Smaller::class,
'lt' => Smaller::class,
'>=' => GreaterOrEqual::class,
'ge' => GreaterOrEqual::class,
'<=' => SmallerOrEqual::class,
'le' => SmallerOrEqual::class,
];

public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Changes use of call to version compare function to use of PHP version constant', [
new CodeSample(
<<<'EOS'
class SomeClass
{
public function run()
{
version_compare(PHP_VERSION, '5.3.0', '<');
}
}
EOS
,
<<<'EOS'
class SomeClass
{
public function run()
{
PHP_VERSION_ID < 50300;
}
}
EOS
),
]);
}

/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}

/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
if (!$this->isName($node, 'version_compare')) {
return null;
}

if (count($node->args) !== 3) {
return null;
}

if (!$this->isPhpVersionConstant($node->args[0]->value) && !$this->isPhpVersionConstant($node->args[1]->value)) {
return null;
}

$left = $this->getNewNodeForArg($node->args[0]->value);
$right = $this->getNewNodeForArg($node->args[1]->value);

/** @var String_ $operator */
$operator = $node->args[2]->value;
$comparison = $this->operatorToComparison[$operator->value];

return new $comparison($left, $right);
}

private function isPhpVersionConstant(Expr $value): bool
{
if ($value instanceof ConstFetch && $value->name->toString() === 'PHP_VERSION') {
return true;
}

return false;
}

private function getNewNodeForArg(Expr $value): Node
{
if ($this->isPhpVersionConstant($value)) {
return new ConstFetch(new Name('PHP_VERSION_ID'));
}

return $this->getVersionNumberFormVersionString($value);
}

private function getVersionNumberFormVersionString(Expr $value): LNumber
{
if (! $value instanceof String_) {
throw new ShouldNotHappenException();
}

if (! preg_match('/^\d+\.\d+\.\d+$/', $value->value)) {
throw new ShouldNotHappenException();
}

$versionParts = explode('.', $value->value);

return new LNumber((int) $versionParts[0] * 10000 + (int) $versionParts[1] * 100 + (int) $versionParts[2]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Rector\CodingStyle\Tests\Rector\FuncCall\FunctionCallToConstantRector\Fixture;

class SomeClass
{
public function run()
{
$value = pi();

$value2 = php_sapi_name();
}
}

?>
-----
<?php

namespace Rector\CodingStyle\Tests\Rector\FuncCall\FunctionCallToConstantRector\Fixture;

class SomeClass
{
public function run()
{
$value = M_PI;

$value2 = PHP_SAPI;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php declare(strict_types=1);

namespace Rector\CodingStyle\Tests\Rector\FuncCall\FunctionCallToConstantRector;

use Iterator;
use Rector\Autodiscovery\Rector\FileSystem\MoveServicesBySuffixToDirectoryRector;
use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector;
use Rector\CodingStyle\Rector\FuncCall\FunctionCallToConstantRector;
use Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class FunctionCallToConstantRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideDataForTest()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}

public function provideDataForTest(): Iterator
{
yield [__DIR__ . '/Fixture/fixture.php.inc'];

}

protected function getRectorsWithConfiguration(): array
{
return [
FunctionCallToConstantRector::class => [
'$functionsToConstants' => [
'php_sapi_name' => 'PHP_SAPI',
'pi' => 'M_PI'
],
],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Rector\CodingStyle\Tests\Rector\FuncCall\VersionCompareFuncCallToConstantRector\Fixture;

class SkipVersionCompareClass
{
public function run()
{
version_compare($this->version, '5.3.0', '<');
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Rector\CodingStyle\Tests\Rector\FuncCall\VersionCompareFuncCallToConstantRector\Fixture;

class VersionCompareClass
{
public function run()
{
version_compare(PHP_VERSION, '5.3.0', '<');
version_compare(PHP_VERSION, '5.3.0', '>');
version_compare(PHP_VERSION, '5.3.0', '=');

version_compare('5.3.0', PHP_VERSION, 'lt');
version_compare('5.3.0', PHP_VERSION, 'gt');
version_compare('5.3.0', PHP_VERSION, 'eq');
}
}

?>
-----
<?php

namespace Rector\CodingStyle\Tests\Rector\FuncCall\VersionCompareFuncCallToConstantRector\Fixture;

class VersionCompareClass
{
public function run()
{
PHP_VERSION_ID < 50300;
PHP_VERSION_ID > 50300;
PHP_VERSION_ID === 50300;

50300 < PHP_VERSION_ID;
50300 > PHP_VERSION_ID;
50300 === PHP_VERSION_ID;
}
}

?>
Loading