Skip to content
Open
Show file tree
Hide file tree
Changes from 18 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ composer.phar
/phpunit.phar
/phpunit.xml
/.phpunit.cache
/.phpunit.result.cache

# PHPBench
.phpbench
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 2.7.1 under development

- no changes in this release.
- New #156: Add `NumericHelper::trimDecimalZeros()` (@samdark)

## 2.7.0 November 23, 2025

Expand Down
46 changes: 45 additions & 1 deletion src/NumericHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
}

/**
* Converts human readable size to bytes.
* Converts human-readable size to bytes.
*
* @param string $string Human readable size. Examples: `1024`, `1kB`, `1.5M`, `1GiB`. Full
* list of supported postfixes in {@see FILESYSTEM_SIZE_POSTFIXES}.
Expand Down Expand Up @@ -157,9 +157,53 @@
throw new InvalidArgumentException("Not supported postfix '$postfix' in input string: $string");
}

return (int) ((float) $numericPart * $postfixMultiplier);

Check warning on line 160 in src/NumericHelper.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "CastFloat": @@ @@ if ($postfixMultiplier === null) { throw new InvalidArgumentException("Not supported postfix '{$postfix}' in input string: {$string}"); } - return (int) ((float) $numericPart * $postfixMultiplier); + return (int) ($numericPart * $postfixMultiplier); } throw new InvalidArgumentException("Incorrect input string: {$string}"); }
}

throw new InvalidArgumentException("Incorrect input string: $string");
}

/**
* Trims trailing decimal zeros from a numeric-like string.
*
* If the fractional part consists only of zeros, the decimal separator is removed as well.
* The value that is `null` or empty is returned as-is.
*
* @param string|null $value String representation of a number or any string potentially
* containing a decimal part.
*
* @return string|null The input string with trailing decimal zeros (and a trailing decimal
* separator, if any) removed, or `null` if the input was `null` or an empty string.
*/
public static function trimDecimalZeros(?string $value): ?string
{
if ($value === null) {
return null;
}

$trimmedValue = trim($value);
if ($trimmedValue === '' || $trimmedValue === '.') {
return $value;
}

if (preg_match('/^(.+)\s\.0+$/', $value, $matches) === 1) {
return $matches[1] . ' 0';
}

if (!str_contains($value, '.')) {
return $value;
}
/** @psalm-suppress PossiblyNullArgument */
$value = rtrim($value, '0');

if ($value === '.') {
return '0';
}

if (!str_ends_with($value, '.')) {
return $value;
}

return substr($value, 0, -1);
}
}
35 changes: 35 additions & 0 deletions tests/NumericHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,39 @@ public function testConvertHumanReadableSizeToBytesWithInvalidStrings(string $st
$this->expectExceptionObject(new \InvalidArgumentException($message));
NumericHelper::convertHumanReadableSizeToBytes($string);
}

public static function dataTrimDecimalZeros(): array
{
return [
'no decimals in integer with zeros' => ['390', '390'],
'all zeros' => ['390.000', '390'],
'no zeros' => ['3.14', '3.14'],
'some zeros' => ['42.010', '42.01'],
'zeros' => ['0.0', '0'],
'decimal only' => ['.5', '.5'],
'decimal zero' => ['.0', '0'],
'start with zero' => ['0.25', '0.25'],
'negative' => ['-3.000', '-3'],

'not numeric with int' => ['hello 42', 'hello 42'],
'not numeric with decimals' => ['hello 3.00', 'hello 3'],
'not numeric with decimal zero' => ['hello .0', 'hello 0'],
'not numeric' => ['hello', 'hello'],

// edge cases
'null' => [null, null],
'empty' => ['', ''],
'spaces' => [' ', ' '],
'dot' => ['.', '.'],
'dot and zero with spaces' => ['. 00', '. '],
'decimal in front of non numeric' => ['3.00 hello', '3.00 hello'],
'dot zero in front of non numeric' => ['hello .00', 'hello 0'],
];
}

#[DataProvider('dataTrimDecimalZeros')]
public function testTrimDecimalZeros(?string $input, ?string $expected): void
{
$this->assertSame($expected, NumericHelper::trimDecimalZeros($input));
}
}
12 changes: 12 additions & 0 deletions tests/benchmarks/NumericHelperBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,16 @@ public function benchNormalize(): void
NumericHelper::normalize('1,000,000.123');
NumericHelper::normalize('1 000 000,123');
}

public function benchTrimDecimalZeros(): void
{
NumericHelper::trimDecimalZeros('390');
NumericHelper::trimDecimalZeros('390.000');
NumericHelper::trimDecimalZeros('3.14');
NumericHelper::trimDecimalZeros('42.010');
NumericHelper::trimDecimalZeros('0.0');
NumericHelper::trimDecimalZeros('.5');
NumericHelper::trimDecimalZeros('0.25');
NumericHelper::trimDecimalZeros('-3.000');
}
}
Loading