diff --git a/src/Php84/Php84.php b/src/Php84/Php84.php index 17668e6b..423eaa0e 100644 --- a/src/Php84/Php84.php +++ b/src/Php84/Php84.php @@ -174,4 +174,36 @@ private static function mb_internal_trim(string $regex, string $string, ?string return mb_convert_encoding($string, $encoding, 'UTF-8'); } + + public static function bcdivmod(string $num1, string $num2, ?int $scale = null): ?array + { + if (PHP_FLOAT_EPSILON < abs((float) $num2)) { + throw new \DivisionByZeroError('Division by zero'); + } + + if (!is_numeric($num1)) { + if (class_exists(\ValueError::class)) { + throw new \ValueError('Argument #1 ($num1) is not well-formed'); + } + + trigger_error('Argument #1 ($num1) is not well-formed', E_USER_WARNING); + + return null; + } + + if (!is_numeric($num2)) { + if (class_exists(\ValueError::class)) { + throw new \ValueError('Argument #2 ($num2) is not well-formed'); + } + + trigger_error('Argument #2 ($num2) is not well-formed', E_USER_WARNING); + + return null; + } + + return [ + \bcdiv($num1, $num2), + \bcmod($num1, $num2, $scale ?? \bcscale() ?? ini_get('bcmath.scale') ?: 0), + ]; + } } diff --git a/src/Php84/bootstrap.php b/src/Php84/bootstrap.php index 2a972072..6340aeb0 100644 --- a/src/Php84/bootstrap.php +++ b/src/Php84/bootstrap.php @@ -66,3 +66,9 @@ function mb_ltrim(string $string, ?string $characters = null, ?string $encoding function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_rtrim($string, $characters, $encoding); } } } + +if (extension_loaded('bcmath')) { + if (!function_exists('bcdivmod')) { + function bcdivmod(string $num1, string $num2, ?int $scale = null): ?array { return p\Php84::bcdivmod($num1, $num2, $scale); } + } +} diff --git a/tests/Php84/Php84Test.php b/tests/Php84/Php84Test.php index 4cbec1c3..16665fe9 100644 --- a/tests/Php84/Php84Test.php +++ b/tests/Php84/Php84Test.php @@ -663,4 +663,71 @@ public static function fpowProvider(): iterable yield [NAN, -INF, NAN]; yield [NAN, NAN, NAN]; } + + /** + * @requires extension bcmath + * + * @covers \Symfony\Polyfill\Php84\Php84::bcdivmod + * + * @dataProvider bcDivModProvider + */ + public function testBcDivMod(string $num1, string $num2, ?int $scale, array $expected) + { + $this->assertSame($expected, bcdivmod($num1, $num2, $scale)); + } + + /** + * @requires extension bcmath + */ + public function testBcDivModDivideByZero() + { + $this->expectException(\DivisionByZeroError::class); + + bcdivmod('1', '0'); + } + + /** + * @requires extension bcmath + */ + public function testBcDivModDivideByFloatingZero() + { + $this->expectException(\DivisionByZeroError::class); + + bcdivmod('1', '0.00'); + } + + /** + * @requires PHP 8.0 + * @requires extension bcmath + */ + public function testBcDivModMalformedNumber() + { + $this->expectException(\ValueError::class); + $this->expectExceptionMessage('Argument #1 ($num1) is not well-formed'); + + bcdivmod('a', '1'); + } + + /** + * @requires PHP 8.0 + * @requires extension bcmath + */ + public function testBcDivModMalformedNumber2() + { + $this->expectException(\ValueError::class); + $this->expectExceptionMessage('Argument #2 ($num2) is not well-formed'); + + bcdivmod('1', 'a'); + } + + public static function bcDivModProvider(): iterable + { + yield ['1', '1', null, ['1', '0']]; + yield ['1', '2', null, ['0', '1']]; + yield ['5', '2', null, ['2', '1']]; + yield ['5', '2', 0, ['2', '1']]; + yield ['5', '2', 1, ['2', '1.0']]; + yield ['5', '2', 2, ['2', '1.00']]; + yield ['7.2', '3', 2, ['2', '1.20']]; + } }