From d8b9dc1a0007ec26c8f9104f402aefefaa974f82 Mon Sep 17 00:00:00 2001 From: hafung <32428762+hafung@users.noreply.github.com> Date: Fri, 28 Aug 2026 10:00:43 +0800 Subject: [PATCH 1/2] fix(optimizer): convert dynamic array_keys strict flag --- src/Optimizer/FuncCallOptimizer.php | 3 +- .../stdlib/array-keys-dynamic-arguments.phpt | 89 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 tests/compiler/stdlib/array-keys-dynamic-arguments.phpt diff --git a/src/Optimizer/FuncCallOptimizer.php b/src/Optimizer/FuncCallOptimizer.php index e5572bdc..4e60b055 100644 --- a/src/Optimizer/FuncCallOptimizer.php +++ b/src/Optimizer/FuncCallOptimizer.php @@ -770,7 +770,8 @@ trait FuncCallOptimizer { $cnt = count($e->args); if ($cnt >= 3) { - return 'php::fn::array_keys_filter(' . $this->getArg($e, 0) . ', ' . $this->getArg($e, 1) . ', ' . $this->getArg($e, 2) . ')'; + return 'php::fn::array_keys_filter(' . $this->getArg($e, 0) . ', ' . $this->getArg($e, 1) . ', ' + . $this->resolveArg($e, 2, self::ARG_TYPE_BOOL) . ')'; } if ($cnt >= 2) { return 'php::fn::array_keys_filter(' . $this->getArg($e, 0) . ', ' . $this->getArg($e, 1) . ', false)'; diff --git a/tests/compiler/stdlib/array-keys-dynamic-arguments.phpt b/tests/compiler/stdlib/array-keys-dynamic-arguments.phpt new file mode 100644 index 00000000..b89e4cbd --- /dev/null +++ b/tests/compiler/stdlib/array-keys-dynamic-arguments.phpt @@ -0,0 +1,89 @@ +--TEST-- +array_keys optimized calls convert dynamic arguments and preserve evaluation order +--FILE-- + 1, 'string' => '1']; +} + +function arrayKeysDynamicFilter(array &$events): mixed +{ + $events[] = 'filter'; + return '1'; +} + +function main() +{ + $values = ['integer' => 1, 'string' => '1']; + + var_dump(array_keys($values)); + var_dump(array_keys($values, '1')); + var_dump(array_keys($values, '1', true)); + + $strict = true; + var_dump(array_keys($values, '1', $strict)); + + $options = new ArrayKeysOptions(); + var_dump(array_keys($values, '1', $options->strict)); + + $events = []; + var_dump(array_keys( + arrayKeysDynamicValues($events), + arrayKeysDynamicFilter($events), + arrayKeysDynamicStrict($events) + )); + var_dump($events); +} +?> +--EXPECT-- +array(2) { + [0]=> + string(7) "integer" + [1]=> + string(6) "string" +} +array(2) { + [0]=> + string(7) "integer" + [1]=> + string(6) "string" +} +array(1) { + [0]=> + string(6) "string" +} +array(1) { + [0]=> + string(6) "string" +} +array(1) { + [0]=> + string(6) "string" +} +array(1) { + [0]=> + string(6) "string" +} +array(3) { + [0]=> + string(5) "array" + [1]=> + string(6) "filter" + [2]=> + string(6) "strict" +} From d480c5e7cbe9e9f00967ca8c55acb4c704bd911d Mon Sep 17 00:00:00 2001 From: hafung <32428762+hafung@users.noreply.github.com> Date: Fri, 28 Aug 2026 13:47:16 +0800 Subject: [PATCH 2/2] fix(optimizer): preserve array_keys strict type errors --- src/Optimizer/FuncCallOptimizer.php | 5 +- .../stdlib/array-keys-dynamic-arguments.phpt | 52 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/Optimizer/FuncCallOptimizer.php b/src/Optimizer/FuncCallOptimizer.php index 4e60b055..6d06b9eb 100644 --- a/src/Optimizer/FuncCallOptimizer.php +++ b/src/Optimizer/FuncCallOptimizer.php @@ -766,10 +766,13 @@ trait FuncCallOptimizer return 'php::fn::get_parent_class(' . $this->parseIdentifier($arg) . ')'; } - protected function genArrayKeys(string $n, Node\Expr\FuncCall $e, array $c): string + protected function genArrayKeys(string $n, Node\Expr\FuncCall $e, array $c): string|false { $cnt = count($e->args); if ($cnt >= 3) { + if ($this->detectTypeOfExpr($e->args[2]->value) !== Type::BOOL) { + return false; + } return 'php::fn::array_keys_filter(' . $this->getArg($e, 0) . ', ' . $this->getArg($e, 1) . ', ' . $this->resolveArg($e, 2, self::ARG_TYPE_BOOL) . ')'; } diff --git a/tests/compiler/stdlib/array-keys-dynamic-arguments.phpt b/tests/compiler/stdlib/array-keys-dynamic-arguments.phpt index b89e4cbd..eb156c36 100644 --- a/tests/compiler/stdlib/array-keys-dynamic-arguments.phpt +++ b/tests/compiler/stdlib/array-keys-dynamic-arguments.phpt @@ -1,5 +1,5 @@ --TEST-- -array_keys optimized calls convert dynamic arguments and preserve evaluation order +array_keys optimized calls preserve dynamic arguments, strict types, and evaluation order --FILE-- 1, 'string' => '1']; @@ -48,6 +68,29 @@ function main() arrayKeysDynamicStrict($events) )); var_dump($events); + + var_dump(array_keys($values, '1', arrayKeysMixedBool())); + + try { + array_keys($values, '1', arrayKeysMixedInt()); + echo "mixed-int=missing TypeError\n"; + } catch (TypeError $error) { + echo "mixed-int=TypeError\n"; + } + + try { + array_keys($values, '1', arrayKeysMixedArray()); + echo "mixed-array=missing TypeError\n"; + } catch (TypeError $error) { + echo "mixed-array=TypeError\n"; + } + + try { + array_keys($values, '1', arrayKeysUnionInt()); + echo "union-int=missing TypeError\n"; + } catch (TypeError $error) { + echo "union-int=TypeError\n"; + } } ?> --EXPECT-- @@ -87,3 +130,10 @@ array(3) { [2]=> string(6) "strict" } +array(1) { + [0]=> + string(6) "string" +} +mixed-int=TypeError +mixed-array=TypeError +union-int=TypeError