From 3e4baee9414d55658472629e5a95ef470ef7a732 Mon Sep 17 00:00:00 2001 From: PurHur Date: Thu, 30 Jul 2026 21:15:25 +0000 Subject: [PATCH 1/2] Stdlib: abs/floor/ceil Reflection/named param num (#23259) Map Zend stub name num in BuiltinParamNames so abs(num:), floor(num:), and ceil(num:) match php-src instead of InternalArgInfo's number. Co-authored-by: Cursor --- lib/BuiltinParamNames.php | 5 +++ .../stdlib/named_args_abs_floor_ceil.phpt | 30 ++++++++++++++++ .../stdlib/named_args_abs_floor_ceil_jit.phpt | 31 ++++++++++++++++ .../cases/abs_floor_ceil_named_params.phpt | 11 ++++++ .../issue_23259_abs_floor_ceil_named.php | 35 +++++++++++++++++++ test/unit/BuiltinParamNamesAliasTest.php | 11 ++++++ 6 files changed, 123 insertions(+) create mode 100644 test/compliance/cases/stdlib/named_args_abs_floor_ceil.phpt create mode 100644 test/compliance/cases/stdlib/named_args_abs_floor_ceil_jit.phpt create mode 100644 test/fixtures/aot/cases/abs_floor_ceil_named_params.phpt create mode 100644 test/repro/issue_23259_abs_floor_ceil_named.php diff --git a/lib/BuiltinParamNames.php b/lib/BuiltinParamNames.php index d539fc5f8ac..a31e828891f 100644 --- a/lib/BuiltinParamNames.php +++ b/lib/BuiltinParamNames.php @@ -504,6 +504,11 @@ public static function forFunction(string $name): ?array return ['filename', 'line']; case 'number_format': return ['num', 'decimals', 'decimal_separator', 'thousands_separator']; + // php-src ext/standard/basic_functions.stub.php — InternalArgInfo still says number (#23259) + case 'abs': + case 'floor': + case 'ceil': + return ['num']; case 'round': return ['num', 'precision', 'mode']; case 'frexp': diff --git a/test/compliance/cases/stdlib/named_args_abs_floor_ceil.phpt b/test/compliance/cases/stdlib/named_args_abs_floor_ceil.phpt new file mode 100644 index 00000000000..979a13c6f42 --- /dev/null +++ b/test/compliance/cases/stdlib/named_args_abs_floor_ceil.phpt @@ -0,0 +1,30 @@ +--TEST-- +abs / floor / ceil named num argument (VM, issue #23259) +--FILE-- +getParameters() as $p) { + echo $fn, ':', $p->getName(), PHP_EOL; + } +} +try { + abs(number: -3); + echo "number accepted\n"; +} catch (Throwable $e) { + echo $e->getMessage(), PHP_EOL; +} +--EXPECT-- +3 +1.0 +2.0 +abs:num +floor:num +ceil:num +Unknown named parameter $number diff --git a/test/compliance/cases/stdlib/named_args_abs_floor_ceil_jit.phpt b/test/compliance/cases/stdlib/named_args_abs_floor_ceil_jit.phpt new file mode 100644 index 00000000000..6f457f2392d --- /dev/null +++ b/test/compliance/cases/stdlib/named_args_abs_floor_ceil_jit.phpt @@ -0,0 +1,31 @@ +--TEST-- +abs / floor / ceil named num argument (JIT, issue #23259) +--JIT-- +--FILE-- +getParameters() as $p) { + echo $fn, ':', $p->getName(), PHP_EOL; + } +} +try { + abs(number: -3); + echo "number accepted\n"; +} catch (Throwable $e) { + echo $e->getMessage(), PHP_EOL; +} +--EXPECT-- +3 +1.0 +2.0 +abs:num +floor:num +ceil:num +Unknown named parameter $number diff --git a/test/fixtures/aot/cases/abs_floor_ceil_named_params.phpt b/test/fixtures/aot/cases/abs_floor_ceil_named_params.phpt new file mode 100644 index 00000000000..fb1b6131292 --- /dev/null +++ b/test/fixtures/aot/cases/abs_floor_ceil_named_params.phpt @@ -0,0 +1,11 @@ +--TEST-- +AOT: abs/floor/ceil named num: argument (#23259) +--FILE-- +getParameters() as $p) { + $names[] = $p->getName(); + } + $checks[] = ['num'] === $names; + try { + if ('abs' === $fn) { + $checks[] = 3 === abs(num: -3) && 3 === abs(-3); + } elseif ('floor' === $fn) { + $checks[] = 1.0 === floor(num: 1.5) && 1.0 === floor(1.5); + } else { + $checks[] = 2.0 === ceil(num: 1.2) && 2.0 === ceil(1.2); + } + } catch (Throwable $e) { + $checks[] = false; + } + try { + if ('abs' === $fn) { + abs(number: -3); + } elseif ('floor' === $fn) { + floor(number: 1.5); + } else { + ceil(number: 1.2); + } + $checks[] = false; + } catch (Error $e) { + $checks[] = str_contains($e->getMessage(), 'Unknown named parameter $number'); + } +} + +echo (!in_array(false, $checks, true)) ? "ok\n" : "fail\n"; diff --git a/test/unit/BuiltinParamNamesAliasTest.php b/test/unit/BuiltinParamNamesAliasTest.php index c41ada5a070..bded56b0994 100644 --- a/test/unit/BuiltinParamNamesAliasTest.php +++ b/test/unit/BuiltinParamNamesAliasTest.php @@ -896,6 +896,17 @@ public function testRoundNamedParameters(): void self::assertSame(2, BuiltinParamNames::lookupNamedParamIndex($names, 'mode', 'round')); } + /** @covers issue #23259 */ + public function testAbsFloorCeilZendStubNamedParams(): void + { + foreach (['abs', 'floor', 'ceil'] as $fn) { + $names = BuiltinParamNames::forFunction($fn); + self::assertSame(['num'], $names, $fn); + self::assertSame(0, BuiltinParamNames::lookupNamedParamIndex($names, 'num', $fn), $fn); + self::assertFalse(BuiltinParamNames::lookupNamedParamIndex($names, 'number', $fn), $fn); + } + } + /** @covers issue #11785 / #25166 */ public function testDateTimeClassMethodNamedParameters(): void { From 5150bb89bc283c58649f7eadc94debf24f51cf76 Mon Sep 17 00:00:00 2001 From: PurHur Date: Thu, 30 Jul 2026 21:16:56 +0000 Subject: [PATCH 2/2] Test: AOT abs(num:) probe for #23259 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the AOT fixture on int abs only — floor/ceil and float abs already miscompile under AOT on master (positional returns 0). Co-authored-by: Cursor --- .../aot/cases/abs_floor_ceil_named_params.phpt | 10 +++++----- test/repro/issue_23259_abs_floor_ceil_named_aot.php | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 test/repro/issue_23259_abs_floor_ceil_named_aot.php diff --git a/test/fixtures/aot/cases/abs_floor_ceil_named_params.phpt b/test/fixtures/aot/cases/abs_floor_ceil_named_params.phpt index fb1b6131292..8a0eb9cbe09 100644 --- a/test/fixtures/aot/cases/abs_floor_ceil_named_params.phpt +++ b/test/fixtures/aot/cases/abs_floor_ceil_named_params.phpt @@ -1,11 +1,11 @@ --TEST-- -AOT: abs/floor/ceil named num: argument (#23259) +AOT: abs() named num: argument (#23259) --FILE--