From 04a7da322afb37eff940d47a3983e16a353b4f94 Mon Sep 17 00:00:00 2001 From: PurHur Date: Sun, 7 Jun 2026 18:18:28 +0000 Subject: [PATCH] fix(stdlib): str_shuffle() TypeError for non-string operands (#4551) Use VmString::coerceStringBuiltinArg and JitStringBuiltinArg so array/object operands match php-src ext/standard/string.c instead of silently accepting non-strings or throwing LogicException. Co-authored-by: Cursor --- ext/standard/str_shuffle.php | 25 ++++++++++++------- .../cases/stdlib/str_shuffle_type_error.phpt | 21 ++++++++++++++++ .../stdlib/str_shuffle_type_error_jit.phpt | 14 +++++++++++ .../aot/cases/str_shuffle_type_error.phpt | 8 ++++++ 4 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 test/compliance/cases/stdlib/str_shuffle_type_error.phpt create mode 100644 test/compliance/cases/stdlib/str_shuffle_type_error_jit.phpt create mode 100644 test/fixtures/aot/cases/str_shuffle_type_error.phpt diff --git a/ext/standard/str_shuffle.php b/ext/standard/str_shuffle.php index 9dc7ae3eb88..518f74652c8 100644 --- a/ext/standard/str_shuffle.php +++ b/ext/standard/str_shuffle.php @@ -7,7 +7,9 @@ use PHPCompiler\Frame; use PHPCompiler\Func\Internal; use PHPCompiler\JIT\Context; +use PHPCompiler\JIT\JitStringBuiltinArg; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\BuiltinExecute; use PHPCompiler\VM\Variable; use PHPLLVM\Value; @@ -21,14 +23,16 @@ public function execute(Frame $frame): void if (1 !== \count($frame->calledArgs)) { throw new \LogicException('str_shuffle() requires exactly one argument'); } - $v = $frame->calledArgs[0]->resolveIndirect(); - if (null === $frame->returnVar) { - return; - } - if (Variable::TYPE_STRING !== $v->type) { - throw new \LogicException('str_shuffle() only supports strings in this compiler build'); - } - $frame->returnVar->string(VmString::strShuffle($v->toString())); + $subject = VmString::coerceStringBuiltinArg( + $frame->calledArgs[0], + 'str_shuffle', + 0, + 'string' + ); + BuiltinExecute::writeReturn( + $frame, + static fn (Variable $ret) => $ret->string(VmString::strShuffle($subject)) + ); } public function call(Context $context, JITVariable ...$args): Value @@ -37,6 +41,9 @@ public function call(Context $context, JITVariable ...$args): Value throw new \LogicException('str_shuffle() requires exactly one argument'); } - return JitStrShuffle::shuffle($context, $this->jitString($context, $args[0], 'str_shuffle() argument #1')); + return JitStrShuffle::shuffle( + $context, + JitStringBuiltinArg::lower($context, $args[0], 'str_shuffle', 0, 'string') + ); } } diff --git a/test/compliance/cases/stdlib/str_shuffle_type_error.phpt b/test/compliance/cases/stdlib/str_shuffle_type_error.phpt new file mode 100644 index 00000000000..1b71622020b --- /dev/null +++ b/test/compliance/cases/stdlib/str_shuffle_type_error.phpt @@ -0,0 +1,21 @@ +--TEST-- +stdlib str_shuffle() — TypeError for non-string operand (#4551, ext/standard/string.c) +--FILE-- +getMessage(), "\n"; +} +try { + $unused = str_shuffle(new stdClass()); + echo "uncaught\n"; +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +echo strlen(str_shuffle('abc')), "\n"; +--EXPECT-- +TypeError: str_shuffle(): Argument #1 ($string) must be of type string, array given +TypeError: str_shuffle(): Argument #1 ($string) must be of type string, stdClass given +3 diff --git a/test/compliance/cases/stdlib/str_shuffle_type_error_jit.phpt b/test/compliance/cases/stdlib/str_shuffle_type_error_jit.phpt new file mode 100644 index 00000000000..f446f9b1e83 --- /dev/null +++ b/test/compliance/cases/stdlib/str_shuffle_type_error_jit.phpt @@ -0,0 +1,14 @@ +--TEST-- +stdlib str_shuffle() JIT — TypeError for non-string operand (#4551) +--FILE-- +getMessage(), "\n"; +} +echo strlen(str_shuffle('abc')), "\n"; +--EXPECT-- +TypeError: str_shuffle(): Argument #1 ($string) must be of type string, array given +3 diff --git a/test/fixtures/aot/cases/str_shuffle_type_error.phpt b/test/fixtures/aot/cases/str_shuffle_type_error.phpt new file mode 100644 index 00000000000..9a42daf8314 --- /dev/null +++ b/test/fixtures/aot/cases/str_shuffle_type_error.phpt @@ -0,0 +1,8 @@ +--TEST-- +AOT: str_shuffle() — TypeError for non-string operand (#4551) +--FILE-- +