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)