Skip to content

Commit

Permalink
[Php84] Add rule for RoundingMode enum (#6369)
Browse files Browse the repository at this point in the history
* Add new rule for rounding modes in PHP 8.4

* Added namespace to test

* Improve tests

* Fixed namespace

* Skip first class callable

* Used FullyQualified instead of name

* Added RoundingModeEnumRector to php84.php

* Removed constructor

* Return null for no change

* cs

* cs

---------

Co-authored-by: Abdul Malik Ikhsan <[email protected]>
  • Loading branch information
jorgsowa and samsonasik authored Dec 20, 2024
1 parent 6aca457 commit a72a021
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 1 deletion.
3 changes: 2 additions & 1 deletion config/set/php84.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Php84\Rector\FuncCall\RoundingModeEnumRector;
use Rector\Php84\Rector\Param\ExplicitNullableParamTypeRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([ExplicitNullableParamTypeRector::class]);
$rectorConfig->rules([ExplicitNullableParamTypeRector::class, RoundingModeEnumRector::class]);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Rector\Tests\Php84\Rector\FuncCall\RoundingModeEnumRector;

round(1.5, 0);
round(1.5);
round(1.5, 0, PHP_ROUND_HALF_UP);
round(1.5, 0, PHP_ROUND_HALF_DOWN);
round(1.5, 0, PHP_ROUND_HALF_EVEN);
round(1.5, 0, PHP_ROUND_HALF_ODD);
round(1.5, 0, 'invalid');
round(1.5, 0, PHP_INT_MAX);

?>
-----
<?php

namespace Rector\Tests\Php84\Rector\FuncCall\RoundingModeEnumRector;

round(1.5, 0);
round(1.5);
round(1.5, 0, \RoundingMode::HalfAwayFromZero);
round(1.5, 0, \RoundingMode::HalfTowardsZero);
round(1.5, 0, \RoundingMode::HalfEven);
round(1.5, 0, \RoundingMode::HalfOdd);
round(1.5, 0, 'invalid');
round(1.5, 0, PHP_INT_MAX);

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

namespace Rector\Tests\Php84\Rector\FuncCall\RoundingModeEnumRector;


$mode = PHP_ROUND_HALF_DOWN;

round(1.5, 0, $mode);

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

declare(strict_types=1);

namespace Rector\Tests\Php84\Rector\FuncCall\RoundingModeEnumRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class RoundingModeEnumRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Php84\Rector\FuncCall\RoundingModeEnumRector;
use Rector\ValueObject\PhpVersion;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(RoundingModeEnumRector::class);

$rectorConfig->phpVersion(PhpVersion::PHP_84);
};
99 changes: 99 additions & 0 deletions rules/Php84/Rector/FuncCall/RoundingModeEnumRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

declare(strict_types=1);

namespace Rector\Php84\Rector\FuncCall;

use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name\FullyQualified;
use Rector\Rector\AbstractRector;
use Rector\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Rector\Tests\Php84\Rector\FuncCall\RoundingModeEnumRector\RoundingModeEnumRectorTest
*/
final class RoundingModeEnumRector extends AbstractRector implements MinPhpVersionInterface
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Replace rounding mode constant to RoundMode enum in round()', [
new CodeSample(
<<<'CODE_SAMPLE'
round(1.5, 0, PHP_ROUND_HALF_UP);
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
round(1.5, 0, RoundingMode::HalfAwayFromZero);
CODE_SAMPLE
,
),
]);
}

public function getNodeTypes(): array
{
return [FuncCall::class];
}

/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?FuncCall
{

if (! $this->isName($node, 'round')) {
return null;
}

if ($node->isFirstClassCallable()) {
return null;
}

$args = $node->getArgs();

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

if (! isset($args[2])) {
return null;
}

$modeArg = $args[2]->value;

$hasChanged = false;
if ($modeArg instanceof ConstFetch) {
$enumCase = match ($modeArg->name->toString()) {
'PHP_ROUND_HALF_UP' => 'HalfAwayFromZero',
'PHP_ROUND_HALF_DOWN' => 'HalfTowardsZero',
'PHP_ROUND_HALF_EVEN' => 'HalfEven',
'PHP_ROUND_HALF_ODD' => 'HalfOdd',
default => null,
};

if ($enumCase === null) {
return null;
}

$args[2]->value = new ClassConstFetch(new FullyQualified('RoundingMode'), $enumCase);
$hasChanged = true;
}

if ($hasChanged) {
return $node;
}

return null;
}

public function provideMinPhpVersion(): int
{
return PhpVersionFeature::ROUNDING_MODES;
}
}
6 changes: 6 additions & 0 deletions src/ValueObject/PhpVersionFeature.php
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,12 @@ final class PhpVersionFeature
*/
public const DEPRECATE_IMPLICIT_NULLABLE_PARAM_TYPE = PhpVersion::PHP_84;

/**
* @see https://wiki.php.net/rfc/correctly_name_the_rounding_mode_and_make_it_an_enum
* @var int
*/
public const ROUNDING_MODES = PhpVersion::PHP_84;

/**
* @see https://www.php.net/manual/en/migration83.deprecated.php#migration83.deprecated.ldap
* @var int
Expand Down

0 comments on commit a72a021

Please sign in to comment.