Commit 51808ac5478 for php
commit 51808ac54782284428e80ffb2543fdff2d663bef
Author: Jorg Sowa <jorg.sowa@gmail.com>
Date: Thu Oct 22 09:50:09 2026 +0200
Fix JIT reading packed-turned-hash array as packed after string key write
Writing a string key into a packed array converts it to a hash with integer
keys, but type inference didn't add MAY_BE_ARRAY_NUMERIC_HASH.
Closes GH-24526.
diff --git a/NEWS b/NEWS
index 952acfa5d8e..568638e2357 100644
--- a/NEWS
+++ b/NEWS
@@ -117,6 +117,8 @@ PHP NEWS
empty array element. (jorgsowa)
. Fixed bug GH-24396 (Assertion failure in zend_jit_def_reg() "reg forwarding"
with chained Pi nodes). (ndossche)
+ . Fixed JIT treating an array as packed after a string key write turned it
+ into a hash. (jorgsowa)
- PDO:
. Fixed invalid ATTR_ORACLE_NULLS values being accepted and truncated.
diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c
index a2e1d03404d..dff89b16f9d 100644
--- a/Zend/Optimizer/zend_inference.c
+++ b/Zend/Optimizer/zend_inference.c
@@ -2224,6 +2224,9 @@ static uint32_t assign_dim_array_result_type(
if (dim_type & (MAY_BE_UNDEF|MAY_BE_NULL)) {
tmp |= MAY_BE_ARRAY_KEY_STRING;
}
+ if ((tmp & MAY_BE_ARRAY_KEY_STRING) && MAY_BE_PACKED(arr_type)) {
+ tmp |= MAY_BE_ARRAY_NUMERIC_HASH;
+ }
}
}
/* Only add value type if we have a key type. It might be that the key type is illegal
@@ -3619,6 +3622,9 @@ static zend_always_inline zend_result _zend_update_type_info(
if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
key_type |= MAY_BE_ARRAY_KEY_STRING;
}
+ if ((key_type & MAY_BE_ARRAY_KEY_STRING) && MAY_BE_PACKED(t1)) {
+ key_type |= MAY_BE_ARRAY_NUMERIC_HASH;
+ }
}
} else if (opline->opcode == ZEND_FETCH_DIM_UNSET) {
if (t1 & MAY_BE_ARRAY) {
diff --git a/ext/opcache/tests/jit/assign_dim_packed_string_key_001.phpt b/ext/opcache/tests/jit/assign_dim_packed_string_key_001.phpt
new file mode 100644
index 00000000000..9147f22354d
--- /dev/null
+++ b/ext/opcache/tests/jit/assign_dim_packed_string_key_001.phpt
@@ -0,0 +1,31 @@
+--TEST--
+JIT: string key write turns a packed array into a hash with integer keys (function JIT)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=64M
+opcache.jit=function
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function f() {
+ $arr = [10, 11];
+ for ($i = 2; $i >= 0; $i--) {
+ $arr['b'] = true;
+ echo $i, ":\n";
+ var_dump($arr[$i]);
+ }
+}
+f();
+?>
+--EXPECTF--
+2:
+
+Warning: Undefined array key 2 in %s on line %d
+NULL
+1:
+int(11)
+0:
+int(10)
diff --git a/ext/opcache/tests/jit/assign_dim_packed_string_key_002.phpt b/ext/opcache/tests/jit/assign_dim_packed_string_key_002.phpt
new file mode 100644
index 00000000000..bbe512d1f47
--- /dev/null
+++ b/ext/opcache/tests/jit/assign_dim_packed_string_key_002.phpt
@@ -0,0 +1,22 @@
+--TEST--
+JIT: compound string key assignment turns a packed array into a hash with integer keys
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=64M
+opcache.jit=function
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function f() {
+ $arr = [1, 2, 3];
+ $arr["a"] += 1;
+ var_dump($arr[1]);
+}
+f();
+?>
+--EXPECTF--
+Warning: Undefined array key "a" in %s on line %d
+int(2)
diff --git a/ext/opcache/tests/jit/assign_dim_packed_string_key_003.phpt b/ext/opcache/tests/jit/assign_dim_packed_string_key_003.phpt
new file mode 100644
index 00000000000..e28dbb4c8e3
--- /dev/null
+++ b/ext/opcache/tests/jit/assign_dim_packed_string_key_003.phpt
@@ -0,0 +1,29 @@
+--TEST--
+JIT: nested string key write turns a packed array into a hash with integer keys (tracing JIT)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=64M
+opcache.jit=tracing
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function f($c) {
+ $arr = [0 => 'x', 5 => 'y'];
+ $arr['a'][32] = 1;
+ $r = 0;
+ for ($i = 0; $i < 100; $i++) {
+ $r += isset($arr[0][$c]);
+ }
+ var_dump($r);
+}
+for ($k = 0; $k < 3; $k++) {
+ f(0);
+}
+?>
+--EXPECT--
+int(100)
+int(100)
+int(100)