limitValue = $limitValue;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('Add "_" as thousands separator in numbers for higher or equals to limitValue config', [new ConfiguredCodeSample(<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$int = 500000;
$float = 1000500.001;
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$int = 500_000;
$float = 1_000_500.001;
}
}
CODE_SAMPLE
, [self::LIMIT_VALUE => 1000000])]);
}
/**
* @return array>
*/
public function getNodeTypes() : array
{
return [LNumber::class, DNumber::class];
}
/**
* @param LNumber|DNumber $node
*/
public function refactor(Node $node) : ?Node
{
$rawValue = $node->getAttribute(AttributeKey::RAW_VALUE);
if ($this->shouldSkip($node, $rawValue)) {
return null;
}
if (\strpos((string) $rawValue, '.') !== \false) {
[$mainPart, $decimalPart] = \explode('.', (string) $rawValue);
$chunks = $this->strSplitNegative($mainPart, self::GROUP_SIZE);
$literalSeparatedNumber = \implode('_', $chunks) . '.' . $decimalPart;
} else {
$chunks = $this->strSplitNegative($rawValue, self::GROUP_SIZE);
$literalSeparatedNumber = \implode('_', $chunks);
// PHP converts: (string) 1000.0 -> "1000"!
if (\is_float($node->value)) {
$literalSeparatedNumber .= '.0';
}
}
// this cannot be integer directly to $node->value, as PHPStan sees it as error type
// @see https://github.com/rectorphp/rector/issues/7454
$node->setAttribute(AttributeKey::RAW_VALUE, $literalSeparatedNumber);
$node->setAttribute(AttributeKey::REPRINT_RAW_VALUE, \true);
$node->setAttribute(AttributeKey::ORIGINAL_NODE, null);
return $node;
}
public function provideMinPhpVersion() : int
{
return PhpVersionFeature::LITERAL_SEPARATOR;
}
/**
* @param \PhpParser\Node\Scalar\LNumber|\PhpParser\Node\Scalar\DNumber $node
* @param mixed $rawValue
*/
private function shouldSkip($node, $rawValue) : bool
{
if (!\is_string($rawValue)) {
return \true;
}
// already contains separator
if (\strpos($rawValue, '_') !== \false) {
return \true;
}
if ($node->value < $this->limitValue) {
return \true;
}
$kind = $node->getAttribute(AttributeKey::KIND);
if (\in_array($kind, [LNumber::KIND_BIN, LNumber::KIND_OCT, LNumber::KIND_HEX], \true)) {
return \true;
}
// e+/e-
if (StringUtils::isMatch($rawValue, '#e#i')) {
return \true;
}
// too short
return \strlen($rawValue) <= self::GROUP_SIZE;
}
/**
* @return string[]
*/
private function strSplitNegative(string $string, int $length) : array
{
$inversed = \strrev($string);
/** @var string[] $chunks */
$chunks = \str_split($inversed, $length);
$chunks = \array_reverse($chunks);
foreach ($chunks as $key => $chunk) {
$chunks[$key] = \strrev($chunk);
}
return $chunks;
}
}