From 34132a7f43a40202a0184f872272d6251838e2c9 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 20 Aug 2025 16:06:28 +0200 Subject: [PATCH 1/5] Deprecate using null as an array offset and when calling `array_key_exists()` --- Zend/tests/assign_dim_op_undef.phpt | 2 + Zend/tests/bug72543_2.phpt | 3 +- Zend/tests/gc/bug67314.phpt | 4 + Zend/tests/isset/isset_array.phpt | 2 + Zend/tests/list/list_keyed_conversions.phpt | 2 + Zend/tests/nullsafe_operator/013.phpt | Bin 2705 -> 2832 bytes Zend/tests/offset_array.phpt | 2 + ...rrayObject_container_offset_behaviour.phpt | 131 +++++++++++++++++ .../array_container_offset_behaviour.phpt | 85 +++++++++++ .../false_container_offset_behaviour.phpt | 85 +++++++++++ .../null_container_offset_behaviour.phpt | 83 +++++++++++ .../unset_empty_isset_comprehensive.phpt | 138 ++++++++++++++++-- Zend/zend_execute.c | 58 +++++++- ext/opcache/jit/zend_jit_helpers.c | 121 +++++++++++++++ ext/opcache/tests/jit/copy_tmp_001.phpt | 5 +- ext/opcache/tests/jit/fetch_dim_r_002.phpt | 4 +- ext/spl/spl_array.c | 1 + ext/spl/tests/bug62978.phpt | 2 + ext/spl/tests/bug70852.phpt | 4 + ext/standard/array.c | 1 + .../tests/array/array_key_exists.phpt | 50 ++++++- .../array/array_key_exists_variation1.phpt | 10 +- .../array/array_key_exists_variation6.phpt | 106 ++++++++++---- ext/standard/tests/array/bug20865.phpt | 5 +- .../array_key_exists_null_deprecation.phpt | 13 ++ .../basic/array_null_offset_deprecation.phpt | 27 ++++ 26 files changed, 902 insertions(+), 42 deletions(-) create mode 100644 tests/basic/array_key_exists_null_deprecation.phpt create mode 100644 tests/basic/array_null_offset_deprecation.phpt diff --git a/Zend/tests/assign_dim_op_undef.phpt b/Zend/tests/assign_dim_op_undef.phpt index 187aed97857a..346311235435 100644 --- a/Zend/tests/assign_dim_op_undef.phpt +++ b/Zend/tests/assign_dim_op_undef.phpt @@ -10,6 +10,8 @@ Warning: Undefined variable $a in %s on line %d Warning: Undefined variable $b in %s on line %d +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d + Warning: Undefined array key "" in %s on line %d array(1) { [""]=> diff --git a/Zend/tests/bug72543_2.phpt b/Zend/tests/bug72543_2.phpt index 2070d65bddc9..fb96cc55449d 100644 --- a/Zend/tests/bug72543_2.phpt +++ b/Zend/tests/bug72543_2.phpt @@ -9,7 +9,8 @@ unset($ref); $arr[0][$arr[0]] = null; var_dump($arr); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d array(1) { [0]=> array(1) { diff --git a/Zend/tests/gc/bug67314.phpt b/Zend/tests/gc/bug67314.phpt index 8077b01881b0..33dc01e90c0a 100644 --- a/Zend/tests/gc/bug67314.phpt +++ b/Zend/tests/gc/bug67314.phpt @@ -17,7 +17,11 @@ echo "ok\n"; ?> --EXPECTF-- Warning: Undefined variable $i in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d made it once Warning: Undefined variable $i in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d ok diff --git a/Zend/tests/isset/isset_array.phpt b/Zend/tests/isset/isset_array.phpt index dfa3fdef51dd..48e908a36d56 100644 --- a/Zend/tests/isset/isset_array.phpt +++ b/Zend/tests/isset/isset_array.phpt @@ -42,6 +42,8 @@ bool(true) Deprecated: Implicit conversion from float 0.6 to int loses precision in %s on line %d bool(true) bool(false) + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d diff --git a/Zend/tests/list/list_keyed_conversions.phpt b/Zend/tests/list/list_keyed_conversions.phpt index 9798be199894..637e517d51b9 100644 --- a/Zend/tests/list/list_keyed_conversions.phpt +++ b/Zend/tests/list/list_keyed_conversions.phpt @@ -21,6 +21,8 @@ list(STDIN => $resource) = []; ?> --EXPECTF-- +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d + Deprecated: Implicit conversion from float 1.5 to int loses precision in %s on line %d string(0) "" int(1) diff --git a/Zend/tests/nullsafe_operator/013.phpt b/Zend/tests/nullsafe_operator/013.phpt index 883f6ac24aa8d1c997bd8dbc3d35290a4473109d..540fcb734c3031a8158b6e01b4ac156399e18a56 100644 GIT binary patch delta 136 zcmX}kF$%&!5Cu?0*do}8*1uE`thM$Ef?XKQsEgUnGBW`eJcJQ+0r62V8UO$Q delta 19 bcmbOrHc@oLdNwB2;>{b_YM3Xl;YtAjMNtOf diff --git a/Zend/tests/offset_array.phpt b/Zend/tests/offset_array.phpt index 368ec7a020e1..e50ffad18cb2 100644 --- a/Zend/tests/offset_array.phpt +++ b/Zend/tests/offset_array.phpt @@ -38,6 +38,8 @@ int(2) Deprecated: Implicit conversion from float 0.0836 to int loses precision in %s on line %d int(1) +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d + Warning: Undefined array key "" in %s on line %d NULL diff --git a/Zend/tests/offsets/ArrayObject_container_offset_behaviour.phpt b/Zend/tests/offsets/ArrayObject_container_offset_behaviour.phpt index 7b3efbbb02de..c44c2df24f77 100644 --- a/Zend/tests/offsets/ArrayObject_container_offset_behaviour.phpt +++ b/Zend/tests/offsets/ArrayObject_container_offset_behaviour.phpt @@ -132,6 +132,93 @@ OUTPUT; $EXPECTED_OUTPUT_FLOAT_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTF_OUTPUT_FLOAT_OFFSETS) . '$/s'; +const EXPECTED_OUTPUT_NULL_OFFSETS = << @@ -841,12 +871,18 @@ array(2) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(2) { [2]=> @@ -861,12 +897,18 @@ array(1) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(1) { [3]=> @@ -877,12 +919,18 @@ array(0) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(0) { } @@ -908,12 +956,18 @@ array(2) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(2) { [1]=> @@ -928,12 +982,18 @@ array(1) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(1) { [2]=> @@ -944,12 +1004,18 @@ array(0) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(0) { } @@ -973,12 +1039,18 @@ array(1) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(1) { [2]=> @@ -989,12 +1061,18 @@ array(0) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(0) { } @@ -1018,12 +1096,18 @@ array(1) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(1) { ["Age"]=> @@ -1034,12 +1118,18 @@ array(0) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(0) { } @@ -1069,12 +1159,18 @@ array(4) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(4) { [1]=> @@ -1097,12 +1193,18 @@ array(3) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(3) { ["One"]=> @@ -1121,12 +1223,18 @@ array(2) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(2) { [2]=> @@ -1141,12 +1249,18 @@ array(1) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(1) { [""]=> @@ -1157,12 +1271,18 @@ array(0) { } Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(false) Warning: Undefined variable $key_val in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d bool(true) array(0) { } @@ -1175,11 +1295,11 @@ bool(true) *** Testing unset(), empty() & isset() with resource variables *** -- Iteration 1 -- -resource(%d) of type (stream) +resource(5) of type (stream) bool(true) bool(false) bool(true) -resource(%d) of type (stream) +resource(5) of type (stream) bool(false) bool(true) bool(false) @@ -1188,11 +1308,11 @@ bool(false) Warning: Undefined variable $resource in %s on line %d NULL -- Iteration 2 -- -resource(%d) of type (stream) +resource(6) of type (stream) bool(true) bool(false) bool(true) -resource(%d) of type (stream) +resource(6) of type (stream) bool(false) bool(true) bool(false) @@ -1207,7 +1327,7 @@ bool(false) bool(true) *** Testing unset(), empty() & isset() with objects *** -object(Point)#%d (3) { +object(Point)#1 (3) { ["x"]=> int(30) ["y"]=> @@ -1229,7 +1349,7 @@ bool(false) Warning: Undefined variable $lable in %s on line %d bool(true) -object(Point)#%d (3) { +object(Point)#1 (3) { ["x"]=> int(30) ["y"]=> @@ -1237,7 +1357,7 @@ object(Point)#%d (3) { ["lable"]=> string(6) "Point1" } -object(Point)#%d (2) { +object(Point)#1 (2) { ["y"]=> int(40) ["lable"]=> @@ -1245,7 +1365,7 @@ object(Point)#%d (2) { } bool(false) bool(true) -object(Point)#%d (0) { +object(Point)#1 (0) { } bool(true) bool(false) @@ -1266,7 +1386,7 @@ array(3) { [2]=> string(9) "testPoint" } -object(Point)#%d (3) { +object(Point)#1 (3) { ["x"]=> int(5) ["y"]=> diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 3908299a41c3..1630aaa9497c 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2629,6 +2629,23 @@ static zend_never_inline uint8_t slow_index_convert(HashTable *ht, const zval *d ZEND_FALLTHROUGH; } case IS_NULL: + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); + + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + return IS_NULL; + } + + if (EG(exception)) { + return IS_NULL; + } + value->str = ZSTR_EMPTY_ALLOC(); return IS_STRING; case IS_DOUBLE: @@ -2699,6 +2716,23 @@ static zend_never_inline uint8_t slow_index_convert_w(HashTable *ht, const zval ZEND_FALLTHROUGH; } case IS_NULL: + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); + + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) { + if (!GC_REFCOUNT(ht)) { + zend_array_destroy(ht); + } + return IS_NULL; + } + if (EG(exception)) { + return IS_NULL; + } value->str = ZSTR_EMPTY_ALLOC(); return IS_STRING; case IS_DOUBLE: @@ -3189,7 +3223,24 @@ static zend_never_inline zval* ZEND_FASTCALL zend_find_array_dim_slow(HashTable num_idx: return zend_hash_index_find(ht, hval); } else if (Z_TYPE_P(offset) == IS_NULL) { -str_idx: +null_undef_idx: + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); + + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + return NULL; + } + + if (EG(exception)) { + return NULL; + } + return zend_hash_find_known_hash(ht, ZSTR_EMPTY_ALLOC()); } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; @@ -3203,7 +3254,7 @@ static zend_never_inline zval* ZEND_FASTCALL zend_find_array_dim_slow(HashTable goto num_idx; } else if (/*OP2_TYPE == IS_CV &&*/ Z_TYPE_P(offset) == IS_UNDEF) { ZVAL_UNDEFINED_OP2(); - goto str_idx; + goto null_undef_idx; } else { zend_illegal_array_offset_isset(offset); return NULL; @@ -3324,6 +3375,9 @@ static zend_never_inline bool ZEND_FASTCALL zend_array_key_exists_fast(HashTable } else if (Z_TYPE_P(key) <= IS_NULL) { if (UNEXPECTED(Z_TYPE_P(key) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); + } else { + ZEND_ASSERT(Z_TYPE_P(key) == IS_NULL); + zend_error(E_DEPRECATED, "Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead"); } str = ZSTR_EMPTY_ALLOC(); goto str_key; diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index b20afffa47df..659862a8e424 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -500,6 +500,31 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_r_helper(zend_array *ht, zval *dim, } ZEND_FALLTHROUGH; case IS_NULL: + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + execute_data = EG(current_execute_data); + opline = EX(opline); + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + if (EG(exception)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } else { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + return; + } + if (EG(exception)) { + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return; + } offset_key = ZSTR_EMPTY_ALLOC(); goto str_index; case IS_DOUBLE: @@ -642,6 +667,31 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_is_helper(zend_array *ht, zval *dim } ZEND_FALLTHROUGH; case IS_NULL: + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + execute_data = EG(current_execute_data); + opline = EX(opline); + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + if (EG(exception)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } else { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + return; + } + if (EG(exception)) { + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return; + } offset_key = ZSTR_EMPTY_ALLOC(); goto str_index; case IS_DOUBLE: @@ -770,6 +820,19 @@ static int ZEND_FASTCALL zend_jit_fetch_dim_isset_helper(zend_array *ht, zval *d } ZEND_FALLTHROUGH; case IS_NULL: + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + return 0; + } + if (EG(exception)) { + return 0; + } offset_key = ZSTR_EMPTY_ALLOC(); goto str_index; case IS_DOUBLE: @@ -878,6 +941,33 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_rw_helper(zend_array *ht, zval *di } ZEND_FALLTHROUGH; case IS_NULL: + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + execute_data = EG(current_execute_data); + opline = EX(opline); + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) { + if (!GC_REFCOUNT(ht)) { + zend_array_destroy(ht); + } + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + if (EG(exception)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } else { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + return NULL; + } + if (EG(exception)) { + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return NULL; + } offset_key = ZSTR_EMPTY_ALLOC(); goto str_index; case IS_DOUBLE: @@ -1011,6 +1101,37 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim } ZEND_FALLTHROUGH; case IS_NULL: + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + execute_data = EG(current_execute_data); + opline = EX(opline); + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) { + if (!GC_REFCOUNT(ht)) { + zend_array_destroy(ht); + } + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + if (EG(exception)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } else { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + if (opline->opcode == ZEND_ASSIGN_DIM + && ((opline+1)->op1_type & (IS_VAR | IS_TMP_VAR))) { + zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + } + return NULL; + } + if (EG(exception)) { + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return NULL; + } offset_key = ZSTR_EMPTY_ALLOC(); goto str_index; case IS_DOUBLE: diff --git a/ext/opcache/tests/jit/copy_tmp_001.phpt b/ext/opcache/tests/jit/copy_tmp_001.phpt index fa92b6ba21ee..8e3a2a96bd4f 100644 --- a/ext/opcache/tests/jit/copy_tmp_001.phpt +++ b/ext/opcache/tests/jit/copy_tmp_001.phpt @@ -15,7 +15,10 @@ $a = []; $a[test()] ??= 1; var_dump($a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d array(1) { [""]=> int(1) diff --git a/ext/opcache/tests/jit/fetch_dim_r_002.phpt b/ext/opcache/tests/jit/fetch_dim_r_002.phpt index fb7471d7a292..324cc688c222 100644 --- a/ext/opcache/tests/jit/fetch_dim_r_002.phpt +++ b/ext/opcache/tests/jit/fetch_dim_r_002.phpt @@ -30,7 +30,7 @@ $x=2; $y="x"; foo($x.$y); ?> ---EXPECT-- +--EXPECTF-- int(1) int(3) int(2) @@ -38,6 +38,8 @@ int(1) int(3) int(1) int(2) + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d int(4) int(5) int(5) diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 781872cda117..ea21b422229b 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -280,6 +280,7 @@ static zend_result get_hash_key(spl_hash_key *key, spl_array_object *intern, zva try_again: switch (Z_TYPE_P(offset)) { case IS_NULL: + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); key->key = ZSTR_EMPTY_ALLOC(); return SUCCESS; case IS_STRING: diff --git a/ext/spl/tests/bug62978.phpt b/ext/spl/tests/bug62978.phpt index 758649e23169..5c5ca05d366b 100644 --- a/ext/spl/tests/bug62978.phpt +++ b/ext/spl/tests/bug62978.phpt @@ -26,6 +26,8 @@ var_dump($a[$fp]); fclose($fp); ?> --EXPECTF-- +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d + Warning: Undefined array key "epic_magic" in %s on line %d NULL diff --git a/ext/spl/tests/bug70852.phpt b/ext/spl/tests/bug70852.phpt index 44168f990320..008a7c0ca4af 100644 --- a/ext/spl/tests/bug70852.phpt +++ b/ext/spl/tests/bug70852.phpt @@ -8,8 +8,12 @@ var_dump($y[NULL]); var_dump($y[NULL]++); ?> --EXPECTF-- +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d + Warning: Undefined array key "" in %s on line %d NULL +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d + Warning: Undefined array key "" in %s on line %d NULL diff --git a/ext/standard/array.c b/ext/standard/array.c index 981bda9a2225..96795e623cb9 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -6981,6 +6981,7 @@ PHP_FUNCTION(array_key_exists) RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key))); break; case IS_NULL: + zend_error(E_DEPRECATED, "Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead"); RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC())); break; case IS_DOUBLE: diff --git a/ext/standard/tests/array/array_key_exists.phpt b/ext/standard/tests/array/array_key_exists.phpt index 8cc2f9c5207c..c0db558f2582 100644 --- a/ext/standard/tests/array/array_key_exists.phpt +++ b/ext/standard/tests/array/array_key_exists.phpt @@ -82,7 +82,7 @@ try { echo "Done\n"; ?> ---EXPECT-- +--EXPECTF-- *** Testing basic functionalities *** -- Iteration 1 -- bool(true) @@ -104,7 +104,11 @@ bool(true) ** Variation loop 1 ** -- Iteration 1 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -112,7 +116,11 @@ bool(false) bool(true) -- Iteration 2 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -120,7 +128,11 @@ bool(false) bool(true) -- Iteration 3 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -128,7 +140,11 @@ bool(false) bool(true) -- Iteration 4 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -136,7 +152,11 @@ bool(false) bool(true) -- Iteration 5 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -144,7 +164,11 @@ bool(false) bool(false) -- Iteration 6 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -152,7 +176,11 @@ bool(false) bool(false) -- Iteration 7 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -162,7 +190,11 @@ bool(true) ** Variation loop 2 ** -- Iteration 1 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -170,7 +202,11 @@ bool(false) bool(false) -- Iteration 2 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -178,7 +214,11 @@ bool(false) bool(false) -- Iteration 3 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) @@ -186,7 +226,11 @@ bool(false) bool(true) -- Iteration 4 -- bool(true) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(true) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(true) bool(false) bool(true) @@ -194,7 +238,11 @@ bool(false) bool(true) -- Iteration 5 -- bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) bool(false) bool(false) diff --git a/ext/standard/tests/array/array_key_exists_variation1.phpt b/ext/standard/tests/array/array_key_exists_variation1.phpt index e3133cfc8eb1..8259ce74906e 100644 --- a/ext/standard/tests/array/array_key_exists_variation1.phpt +++ b/ext/standard/tests/array/array_key_exists_variation1.phpt @@ -105,9 +105,13 @@ bool(false) bool(false) -- Iteration 5 -- + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) -- Iteration 6 -- + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) -- Iteration 7 -- @@ -144,13 +148,17 @@ bool(true) Cannot access offset of type classA on array -- Iteration 18 -- + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) -- Iteration 19 -- + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(false) -- Iteration 20 -- -Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d +Warning: Resource ID#5 used as offset, casting to integer (5) in %s on line %d bool(false) Done diff --git a/ext/standard/tests/array/array_key_exists_variation6.phpt b/ext/standard/tests/array/array_key_exists_variation6.phpt index fe16478833c5..8c98a6971c02 100644 --- a/ext/standard/tests/array/array_key_exists_variation6.phpt +++ b/ext/standard/tests/array/array_key_exists_variation6.phpt @@ -36,54 +36,110 @@ foreach($array as $name => $input) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing array_key_exists() : usage variations *** -- Key in $search array is : null -- -Iteration 1: bool(true) -Iteration 2: bool(true) + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d +Iteration 1: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 2: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) Iteration 3: bool(true) Iteration 4: bool(true) -Iteration 5: bool(true) -Iteration 6: bool(true) +Iteration 5: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 6: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) -- Key in $search array is : NULL -- -Iteration 1: bool(true) -Iteration 2: bool(true) + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d +Iteration 1: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 2: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) Iteration 3: bool(true) Iteration 4: bool(true) -Iteration 5: bool(true) -Iteration 6: bool(true) +Iteration 5: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 6: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) -- Key in $search array is : empty single quoted string -- -Iteration 1: bool(true) -Iteration 2: bool(true) +Iteration 1: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 2: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) Iteration 3: bool(true) Iteration 4: bool(true) -Iteration 5: bool(true) -Iteration 6: bool(true) +Iteration 5: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 6: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) -- Key in $search array is : empty double quoted string -- -Iteration 1: bool(true) -Iteration 2: bool(true) +Iteration 1: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 2: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) Iteration 3: bool(true) Iteration 4: bool(true) -Iteration 5: bool(true) -Iteration 6: bool(true) +Iteration 5: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 6: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) -- Key in $search array is : undefined variable -- -Iteration 1: bool(true) -Iteration 2: bool(true) + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d +Iteration 1: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 2: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) Iteration 3: bool(true) Iteration 4: bool(true) -Iteration 5: bool(true) -Iteration 6: bool(true) +Iteration 5: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 6: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) -- Key in $search array is : unset variable -- -Iteration 1: bool(true) -Iteration 2: bool(true) + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d +Iteration 1: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 2: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) Iteration 3: bool(true) Iteration 4: bool(true) -Iteration 5: bool(true) -Iteration 6: bool(true) +Iteration 5: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +Iteration 6: +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) Done diff --git a/ext/standard/tests/array/bug20865.phpt b/ext/standard/tests/array/bug20865.phpt index 6dfad2bcec05..7401e98b1ab0 100644 --- a/ext/standard/tests/array/bug20865.phpt +++ b/ext/standard/tests/array/bug20865.phpt @@ -7,5 +7,8 @@ Bug #20865 (array_key_exists and NULL key) var_dump(array_key_exists(NULL, $ta)); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d + +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d bool(true) diff --git a/tests/basic/array_key_exists_null_deprecation.phpt b/tests/basic/array_key_exists_null_deprecation.phpt new file mode 100644 index 000000000000..5087ec3362ae --- /dev/null +++ b/tests/basic/array_key_exists_null_deprecation.phpt @@ -0,0 +1,13 @@ +--TEST-- +Deprecate using null as key in array_key_exists() +--FILE-- + 'bar', '' => 'baz']; + +var_dump(array_key_exists(null, $arr)); +var_dump(array_key_exists('', $arr)); +?> +--EXPECTF-- +Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in %s on line %d +bool(true) +bool(true) diff --git a/tests/basic/array_null_offset_deprecation.phpt b/tests/basic/array_null_offset_deprecation.phpt new file mode 100644 index 000000000000..fdc4134522a1 --- /dev/null +++ b/tests/basic/array_null_offset_deprecation.phpt @@ -0,0 +1,27 @@ +--TEST-- +Deprecate using null as array offset +--FILE-- + 'bar', '' => 'baz']; + +echo $arr[null] . "\n"; + +$arr[null] = 'new_value'; +echo $arr[''] . "\n"; + +var_dump(isset($arr[null])); + +unset($arr[null]); +var_dump(isset($arr[''])); +?> +--EXPECTF-- + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d +baz + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d +new_value + +Deprecated: Using null as an array offset is deprecated, use an empty string instead in %s on line %d +bool(true) +bool(false) From 1bf2dbd0b6204a35b872ca81a43ee13a48d0cb65 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 22 Aug 2025 11:58:25 +0200 Subject: [PATCH 2/5] Avoid addref/delref where possible --- Zend/tests/offsets/null_offset_no_uaf.phpt | 17 ++++ Zend/zend_execute.c | 12 +-- ext/opcache/jit/zend_jit_helpers.c | 99 +++++++--------------- 3 files changed, 49 insertions(+), 79 deletions(-) create mode 100644 Zend/tests/offsets/null_offset_no_uaf.phpt diff --git a/Zend/tests/offsets/null_offset_no_uaf.phpt b/Zend/tests/offsets/null_offset_no_uaf.phpt new file mode 100644 index 000000000000..38a1b9868345 --- /dev/null +++ b/Zend/tests/offsets/null_offset_no_uaf.phpt @@ -0,0 +1,17 @@ +--TEST-- +No UAF on null offset +--FILE-- + +--EXPECTF-- +Using null as an array offset is deprecated, use an empty string instead +Success diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 1630aaa9497c..808c50241ece 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2631,9 +2631,7 @@ static zend_never_inline uint8_t slow_index_convert(HashTable *ht, const zval *d case IS_NULL: /* The array may be destroyed while throwing the notice. * Temporarily increase the refcount to detect this situation. */ - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(ht); - } + GC_TRY_ADDREF(ht); zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); @@ -2718,9 +2716,7 @@ static zend_never_inline uint8_t slow_index_convert_w(HashTable *ht, const zval case IS_NULL: /* The array may be destroyed while throwing the notice. * Temporarily increase the refcount to detect this situation. */ - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(ht); - } + GC_TRY_ADDREF(ht); zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); @@ -3226,9 +3222,7 @@ static zend_never_inline zval* ZEND_FASTCALL zend_find_array_dim_slow(HashTable null_undef_idx: /* The array may be destroyed while throwing the notice. * Temporarily increase the refcount to detect this situation. */ - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(ht); - } + GC_TRY_ADDREF(ht); zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 659862a8e424..a1dc84a3e551 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -500,33 +500,16 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_r_helper(zend_array *ht, zval *dim, } ZEND_FALLTHROUGH; case IS_NULL: - /* The array may be destroyed while throwing the notice. - * Temporarily increase the refcount to detect this situation. */ - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(ht); + retval = zend_hash_find(ht, ZSTR_EMPTY_ALLOC()); + if (!retval) { + ZVAL_NULL(result); + } else { + ZVAL_COPY_DEREF(result, retval); } - execute_data = EG(current_execute_data); - opline = EX(opline); + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { - zend_array_destroy(ht); - if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { - if (EG(exception)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - } else { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - return; - } - if (EG(exception)) { - if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - } - return; - } - offset_key = ZSTR_EMPTY_ALLOC(); - goto str_index; + + return; case IS_DOUBLE: hval = zend_dval_to_lval(Z_DVAL_P(dim)); if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) { @@ -667,33 +650,16 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_is_helper(zend_array *ht, zval *dim } ZEND_FALLTHROUGH; case IS_NULL: - /* The array may be destroyed while throwing the notice. - * Temporarily increase the refcount to detect this situation. */ - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(ht); + retval = zend_hash_find(ht, ZSTR_EMPTY_ALLOC()); + if (!retval) { + ZVAL_NULL(result); + } else { + ZVAL_COPY_DEREF(result, retval); } - execute_data = EG(current_execute_data); - opline = EX(opline); + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { - zend_array_destroy(ht); - if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { - if (EG(exception)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - } else { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - return; - } - if (EG(exception)) { - if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - } - return; - } - offset_key = ZSTR_EMPTY_ALLOC(); - goto str_index; + + return; case IS_DOUBLE: hval = zend_dval_to_lval(Z_DVAL_P(dim)); if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) { @@ -819,22 +785,17 @@ static int ZEND_FASTCALL zend_jit_fetch_dim_isset_helper(zend_array *ht, zval *d return 0; } ZEND_FALLTHROUGH; - case IS_NULL: - /* The array may be destroyed while throwing the notice. - * Temporarily increase the refcount to detect this situation. */ - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(ht); + case IS_NULL: { + int result = 0; + retval = zend_hash_find(ht, ZSTR_EMPTY_ALLOC()); + if (retval) { + result = Z_TYPE_P(retval) > IS_NULL; } + zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { - zend_array_destroy(ht); - return 0; - } - if (EG(exception)) { - return 0; - } - offset_key = ZSTR_EMPTY_ALLOC(); - goto str_index; + + return result; + } case IS_DOUBLE: hval = zend_dval_to_lval(Z_DVAL_P(dim)); if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) { @@ -943,9 +904,8 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_rw_helper(zend_array *ht, zval *di case IS_NULL: /* The array may be destroyed while throwing the notice. * Temporarily increase the refcount to detect this situation. */ - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(ht); - } + GC_TRY_ADDREF(ht); + execute_data = EG(current_execute_data); opline = EX(opline); zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); @@ -1103,9 +1063,8 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim case IS_NULL: /* The array may be destroyed while throwing the notice. * Temporarily increase the refcount to detect this situation. */ - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(ht); - } + GC_TRY_ADDREF(ht); + execute_data = EG(current_execute_data); opline = EX(opline); zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); From 95306bb4cb228c1f42e4a0db9424005212cb701a Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 22 Aug 2025 18:07:48 +0200 Subject: [PATCH 3/5] try fix circleci --- ext/opcache/jit/zend_jit_helpers.c | 33 ++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index a1dc84a3e551..4c0fffb87870 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -500,16 +500,33 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_r_helper(zend_array *ht, zval *dim, } ZEND_FALLTHROUGH; case IS_NULL: - retval = zend_hash_find(ht, ZSTR_EMPTY_ALLOC()); - if (!retval) { - ZVAL_NULL(result); - } else { - ZVAL_COPY_DEREF(result, retval); + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); } - + execute_data = EG(current_execute_data); + opline = EX(opline); zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); - - return; + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + if (EG(exception)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } else { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + return; + } + if (EG(exception)) { + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return; + } + offset_key = ZSTR_EMPTY_ALLOC(); + goto str_index; case IS_DOUBLE: hval = zend_dval_to_lval(Z_DVAL_P(dim)); if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) { From 075dd16e4d52407c4f661cb9d603a105ab3b2632 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 25 Aug 2025 08:30:47 +0200 Subject: [PATCH 4/5] simplify first case --- ext/standard/tests/array/array_key_exists_variation1.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/tests/array/array_key_exists_variation1.phpt b/ext/standard/tests/array/array_key_exists_variation1.phpt index 8259ce74906e..d12fbc839b79 100644 --- a/ext/standard/tests/array/array_key_exists_variation1.phpt +++ b/ext/standard/tests/array/array_key_exists_variation1.phpt @@ -159,6 +159,6 @@ bool(false) -- Iteration 20 -- -Warning: Resource ID#5 used as offset, casting to integer (5) in %s on line %d +Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d bool(false) Done From 3ff691ca8a66fb292fc9b1ad7a6d772a3c893676 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 4 Sep 2025 08:44:25 +0200 Subject: [PATCH 5/5] Apply Niels diff --- ext/opcache/jit/zend_jit_helpers.c | 32 ++++++++---------------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 4c0fffb87870..66cfe11a7915 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -500,33 +500,17 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_r_helper(zend_array *ht, zval *dim, } ZEND_FALLTHROUGH; case IS_NULL: - /* The array may be destroyed while throwing the notice. - * Temporarily increase the refcount to detect this situation. */ - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(ht); + retval = zend_hash_find(ht, ZSTR_EMPTY_ALLOC()); + if (!retval) { + ZVAL_NULL(result); + } else { + ZVAL_COPY_DEREF(result, retval); } - execute_data = EG(current_execute_data); - opline = EX(opline); zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); - if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { - zend_array_destroy(ht); - if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { - if (EG(exception)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - } else { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - return; - } - if (EG(exception)) { - if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - } - return; + if (!retval) { + zend_error(E_WARNING, "Undefined array key \"\""); } - offset_key = ZSTR_EMPTY_ALLOC(); - goto str_index; + return; case IS_DOUBLE: hval = zend_dval_to_lval(Z_DVAL_P(dim)); if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) {