Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion .github/workflows/php-src-bcmath-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ jobs:
cp php-src/ext/bcmath/tests/*.inc tests/php-src/

- name: Run php-src BCMath tests
run: ./scripts/run-php-src-tests.sh --skip gh17398,gh16262,gh15968,bcround_toward_zero,bcround_half_up,bcround_half_odd,bcround_half_even,bcround_half_down,bcround_all,bcround_floor,bcround_early_return,bcround_ceiling,bcround_away_from_zero,bcdivmod,bcpow_error2,bcpowmod_zero_modulus,bug60377,bug78878,bcdiv_error2,bcmod_error3
run: ./scripts/run-php-src-tests.sh --skip gh17398,gh16262,gh15968,bcround_toward_zero,bcround_all,bcround_floor,bcround_early_return,bcround_ceiling,bcround_away_from_zero,bcdivmod,bcpow_error2,bcpowmod_zero_modulus,bug60377,bug78878,bcdiv_error2,bcmod_error3
31 changes: 0 additions & 31 deletions .scrutinizer.yml

This file was deleted.

1 change: 0 additions & 1 deletion .styleci.yml

This file was deleted.

39 changes: 0 additions & 39 deletions .travis.yml

This file was deleted.

2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"ext-gmp": "Will enable faster math operations"
},
"autoload": {
"files": ["lib/bcmath.php"],
"files": ["lib/bcmath.php", "lib/RoundingMode.php"],
"psr-4": {
"bcmath_compat\\": "src"
}
Expand Down
26 changes: 26 additions & 0 deletions lib/RoundingMode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/**
* RoundingMode enum polyfill for PHP 8.1-8.3.
*
* This enum provides the same interface as PHP 8.4's native RoundingMode enum,
* but TowardsZero, AwayFromZero, and NegativeInfinity will throw exceptions
* when used in PHP < 8.4 to maintain compatibility expectations.
*
* The enum is only defined if:
* - PHP version is 8.1 or higher (enum support)
* - Native RoundingMode enum doesn't already exist (PHP < 8.4)
*/
// @phpstan-ignore-next-line
if (!enum_exists('RoundingMode') && version_compare(PHP_VERSION, '8.1', '>=')) {
enum RoundingMode: string
{
case HalfAwayFromZero = 'half_away_from_zero';
case HalfTowardsZero = 'half_towards_zero';
case HalfEven = 'half_even';
case HalfOdd = 'half_odd';
case TowardsZero = 'towards_zero';
case AwayFromZero = 'away_from_zero';
case NegativeInfinity = 'negative_infinity';
}
}
14 changes: 0 additions & 14 deletions phpcs.xml.dist

This file was deleted.

47 changes: 44 additions & 3 deletions src/BCMath.php
Original file line number Diff line number Diff line change
Expand Up @@ -958,9 +958,13 @@ public static function ceil(string $num): string
/**
* Round to a given decimal place.
*
* @param string $num The value to round
* @param int $precision The number of decimal digits to round to
* @param int|\RoundingMode $mode The rounding mode (PHP8.4+ supports RoundingMode enum)
*
* @throws \ValueError if inputs are not well-formed
*/
public static function round(string $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): string
public static function round(string $num, int $precision = 0, $mode = PHP_ROUND_HALF_UP): string
{
self::validateNumberString($num, 'bcround', 1, 'num');

Expand All @@ -973,6 +977,9 @@ public static function round(string $num, int $precision = 0, int $mode = PHP_RO
return '0';
}

// Convert RoundingMode enum to integer constant for PHP 8.4+ compatibility
$roundingMode = self::convertRoundingMode($mode);

// Based on: https://stackoverflow.com/a/1653826
if ($precision < 0) {
// When precision is negative, we round to the left of the decimal point
Expand All @@ -981,13 +988,47 @@ public static function round(string $num, int $precision = 0, int $mode = PHP_RO
$shifted = self::div($num, $factor, 10); // Use a high precision for intermediate calculation

// Apply rounding
$rounded = self::bcroundHelper($shifted, 0, $mode);
$rounded = self::bcroundHelper($shifted, 0, $roundingMode);

// Shift back
return self::mul($rounded, $factor, 0);
}

return self::bcroundHelper($num, $precision, $mode);
return self::bcroundHelper($num, $precision, $roundingMode);
}

/**
* Convert RoundingMode enum to integer constant for backward compatibility.
*
* @param int|\RoundingMode $mode The rounding mode
*
* @return int The corresponding PHP_ROUND_* constant
*
* @throws \ValueError If an invalid rounding mode is provided
*/
private static function convertRoundingMode($mode): int
{
// RoundingMode enum support (both native PHP 8.4+ and polyfill PHP 8.1-8.3)
if (enum_exists('RoundingMode') && $mode instanceof \RoundingMode) {
return match ($mode) {
\RoundingMode::HalfAwayFromZero => PHP_ROUND_HALF_UP,
\RoundingMode::HalfTowardsZero => PHP_ROUND_HALF_DOWN,
\RoundingMode::HalfEven => PHP_ROUND_HALF_EVEN,
\RoundingMode::HalfOdd => PHP_ROUND_HALF_ODD,
// TODO: Support additional modes if needed
\RoundingMode::NegativeInfinity => throw new \ValueError('RoundingMode::NegativeInfinity is not supported'),
\RoundingMode::TowardsZero => throw new \ValueError('RoundingMode::TowardsZero is not supported'),
\RoundingMode::AwayFromZero => throw new \ValueError('RoundingMode::AwayFromZero is not supported'), // @phpstan-ignore-line
Copy link

Copilot AI Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PHPStan ignore comment should include a reason for why it's needed. Consider adding a brief explanation like // @phpstan-ignore-line match.unhandled or similar to clarify why this line needs to be ignored.

Copilot uses AI. Check for mistakes.
Comment on lines +1023 to +1025
Copy link

Copilot AI Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error messages are inconsistent with the test expectations. The tests expect messages ending with 'is not supported' but these say 'is not supported'. Consider adding a consistent suffix like 'in PHP < 8.4' to match the PR description and provide clearer context.

Copilot uses AI. Check for mistakes.
default => throw new \ValueError('Unsupported RoundingMode')
};
}

// Backward compatibility for PHP_ROUND_* constants
if (is_int($mode)) {
return $mode;
}

throw new \ValueError('Invalid rounding mode provided');
}

/**
Expand Down
Loading
Loading