diff --git a/src/Optimizer/FuncCallOptimizer.php b/src/Optimizer/FuncCallOptimizer.php index 73297492..f78f2ff9 100644 --- a/src/Optimizer/FuncCallOptimizer.php +++ b/src/Optimizer/FuncCallOptimizer.php @@ -766,11 +766,15 @@ 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) { - return 'php::fn::array_keys_filter(' . $this->getArg($e, 0) . ', ' . $this->getArg($e, 1) . ', ' . $this->getArg($e, 2) . ')'; + 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) . ')'; } 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..eb156c36 --- /dev/null +++ b/tests/compiler/stdlib/array-keys-dynamic-arguments.phpt @@ -0,0 +1,139 @@ +--TEST-- +array_keys optimized calls preserve dynamic arguments, strict types, and evaluation order +--FILE-- + 1, 'string' => '1']; +} + +function arrayKeysDynamicFilter(array &$events): mixed +{ + $events[] = 'filter'; + return '1'; +} + +function arrayKeysMixedBool(): mixed +{ + return true; +} + +function arrayKeysMixedInt(): mixed +{ + return 1; +} + +function arrayKeysMixedArray(): mixed +{ + return []; +} + +function arrayKeysUnionInt(): bool|int +{ + 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); + + 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-- +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" +} +array(1) { + [0]=> + string(6) "string" +} +mixed-int=TypeError +mixed-array=TypeError +union-int=TypeError