From 643e49d56ef6ffaf9d46dbe095b5d8da850da871 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 3 Sep 2026 12:29:29 +0800 Subject: [PATCH] fix: reject unsupported Decimal round modes --- phpunit/code/round-decimal-native-path.php | 14 +++++++++++++ phpunit/src/NegativeCompatibilityTest.php | 24 ++++++++++++++++++++++ phpunit/src/RoundModeTest.php | 7 +++++++ src/Optimizer/FuncCallOptimizer.php | 7 +++++++ 4 files changed, 52 insertions(+) create mode 100644 phpunit/code/round-decimal-native-path.php diff --git a/phpunit/code/round-decimal-native-path.php b/phpunit/code/round-decimal-native-path.php new file mode 100644 index 00000000..4e4f9ab7 --- /dev/null +++ b/phpunit/code/round-decimal-native-path.php @@ -0,0 +1,14 @@ + [ + 'convert', + 'round() with Decimal supports at most 2 arguments', + <<<'PHP' + [ + 'convert', + 'round() expects at most 3 argument(s), 4 given', + <<<'PHP' + [ 'convert', "Cannot 'break' 2 levels", diff --git a/phpunit/src/RoundModeTest.php b/phpunit/src/RoundModeTest.php index 552538cf..0e983002 100644 --- a/phpunit/src/RoundModeTest.php +++ b/phpunit/src/RoundModeTest.php @@ -56,6 +56,13 @@ class RoundModeTest extends TestCase self::assertSame(2, substr_count($cpp, 'php::fn::round(')); } + public function testDecimalCallsWithoutAModeKeepTheDecimalFastPath(): void + { + $cpp = $this->compileToCpp('round-decimal-native-path.php'); + + self::assertSame(2, substr_count($cpp, 'php::Decimal::round(')); + } + private function compileToCpp(string $file): string { global $translator; diff --git a/src/Optimizer/FuncCallOptimizer.php b/src/Optimizer/FuncCallOptimizer.php index 3af12f9a..df81b167 100644 --- a/src/Optimizer/FuncCallOptimizer.php +++ b/src/Optimizer/FuncCallOptimizer.php @@ -1015,6 +1015,13 @@ trait FuncCallOptimizer } $type = $this->detectTypeOfExpr($e->args[0]->value); if ($type === Type::DECIMAL) { + // Decimal is a PHPX Box resource at the Zend boundary, so the + // generic round() function cannot implement its rounding mode. + // Reject the unsupported extension form instead of silently + // dropping the explicit argument or producing a resource TypeError. + if (count($e->args) > 2) { + $this->fatalError($e, 'round() with Decimal supports at most 2 arguments'); + } $a0 = $this->parseExpr($e->args[0]->value); if (count($e->args) >= 2) { return 'php::Decimal::round(' . $a0 . ', ' . $this->parseExpr($e->args[1]->value) . ')';