Skip to content

Commit 6a195bd

Browse files
committed
Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2: Fix GH-16905: Internal iterator functions can't handle UNDEF properties
2 parents 841e54d + e1b4534 commit 6a195bd

File tree

3 files changed

+145
-51
lines changed

3 files changed

+145
-51
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ PHP NEWS
6969
- SOAP:
7070
. Fix make check being invoked in ext/soap. (Ma27)
7171

72+
- Standard:
73+
. Fixed bug GH-16905 (Internal iterator functions can't handle UNDEF
74+
properties). (nielsdos)
75+
7276
- Windows:
7377
. Fixed bug GH-16849 (Error dialog causes process to hang). (cmb)
7478

ext/standard/array.c

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,11 +1029,50 @@ static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
10291029
return zobj->handlers->get_properties(zobj);
10301030
}
10311031

1032+
static zval *php_array_iter_seek_current(HashTable *array, bool forward_direction)
1033+
{
1034+
zval *entry;
1035+
1036+
while (true) {
1037+
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1038+
return NULL;
1039+
}
1040+
1041+
ZVAL_DEINDIRECT(entry);
1042+
1043+
/* Possible with an uninitialized typed property */
1044+
if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
1045+
zend_result result;
1046+
if (forward_direction) {
1047+
result = zend_hash_move_forward(array);
1048+
} else {
1049+
result = zend_hash_move_backwards(array);
1050+
}
1051+
if (result != SUCCESS) {
1052+
return NULL;
1053+
}
1054+
} else {
1055+
break;
1056+
}
1057+
}
1058+
1059+
return entry;
1060+
}
1061+
1062+
static void php_array_iter_return_current(zval *return_value, HashTable *array, bool forward_direction)
1063+
{
1064+
zval *entry = php_array_iter_seek_current(array, forward_direction);
1065+
if (EXPECTED(entry)) {
1066+
RETURN_COPY_DEREF(entry);
1067+
} else {
1068+
RETURN_FALSE;
1069+
}
1070+
}
1071+
10321072
/* {{{ Advances array argument's internal pointer to the last element and return it */
10331073
PHP_FUNCTION(end)
10341074
{
10351075
zval *array_zv;
1036-
zval *entry;
10371076

10381077
ZEND_PARSE_PARAMETERS_START(1, 1)
10391078
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@@ -1047,15 +1086,7 @@ PHP_FUNCTION(end)
10471086
zend_hash_internal_pointer_end(array);
10481087

10491088
if (USED_RET()) {
1050-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1051-
RETURN_FALSE;
1052-
}
1053-
1054-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1055-
entry = Z_INDIRECT_P(entry);
1056-
}
1057-
1058-
RETURN_COPY_DEREF(entry);
1089+
php_array_iter_return_current(return_value, array, false);
10591090
}
10601091
}
10611092
/* }}} */
@@ -1064,7 +1095,6 @@ PHP_FUNCTION(end)
10641095
PHP_FUNCTION(prev)
10651096
{
10661097
zval *array_zv;
1067-
zval *entry;
10681098

10691099
ZEND_PARSE_PARAMETERS_START(1, 1)
10701100
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@@ -1078,15 +1108,7 @@ PHP_FUNCTION(prev)
10781108
zend_hash_move_backwards(array);
10791109

10801110
if (USED_RET()) {
1081-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1082-
RETURN_FALSE;
1083-
}
1084-
1085-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1086-
entry = Z_INDIRECT_P(entry);
1087-
}
1088-
1089-
RETURN_COPY_DEREF(entry);
1111+
php_array_iter_return_current(return_value, array, false);
10901112
}
10911113
}
10921114
/* }}} */
@@ -1095,7 +1117,6 @@ PHP_FUNCTION(prev)
10951117
PHP_FUNCTION(next)
10961118
{
10971119
zval *array_zv;
1098-
zval *entry;
10991120

11001121
ZEND_PARSE_PARAMETERS_START(1, 1)
11011122
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@@ -1109,15 +1130,7 @@ PHP_FUNCTION(next)
11091130
zend_hash_move_forward(array);
11101131

11111132
if (USED_RET()) {
1112-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1113-
RETURN_FALSE;
1114-
}
1115-
1116-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1117-
entry = Z_INDIRECT_P(entry);
1118-
}
1119-
1120-
RETURN_COPY_DEREF(entry);
1133+
php_array_iter_return_current(return_value, array, true);
11211134
}
11221135
}
11231136
/* }}} */
@@ -1126,7 +1139,6 @@ PHP_FUNCTION(next)
11261139
PHP_FUNCTION(reset)
11271140
{
11281141
zval *array_zv;
1129-
zval *entry;
11301142

11311143
ZEND_PARSE_PARAMETERS_START(1, 1)
11321144
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@@ -1140,15 +1152,7 @@ PHP_FUNCTION(reset)
11401152
zend_hash_internal_pointer_reset(array);
11411153

11421154
if (USED_RET()) {
1143-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1144-
RETURN_FALSE;
1145-
}
1146-
1147-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1148-
entry = Z_INDIRECT_P(entry);
1149-
}
1150-
1151-
RETURN_COPY_DEREF(entry);
1155+
php_array_iter_return_current(return_value, array, true);
11521156
}
11531157
}
11541158
/* }}} */
@@ -1157,22 +1161,13 @@ PHP_FUNCTION(reset)
11571161
PHP_FUNCTION(current)
11581162
{
11591163
zval *array_zv;
1160-
zval *entry;
11611164

11621165
ZEND_PARSE_PARAMETERS_START(1, 1)
11631166
Z_PARAM_ARRAY_OR_OBJECT(array_zv)
11641167
ZEND_PARSE_PARAMETERS_END();
11651168

11661169
HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1167-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1168-
RETURN_FALSE;
1169-
}
1170-
1171-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1172-
entry = Z_INDIRECT_P(entry);
1173-
}
1174-
1175-
RETURN_COPY_DEREF(entry);
1170+
php_array_iter_return_current(return_value, array, true);
11761171
}
11771172
/* }}} */
11781173

@@ -1186,7 +1181,10 @@ PHP_FUNCTION(key)
11861181
ZEND_PARSE_PARAMETERS_END();
11871182

11881183
HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1189-
zend_hash_get_current_key_zval(array, return_value);
1184+
zval *entry = php_array_iter_seek_current(array, true);
1185+
if (EXPECTED(entry)) {
1186+
zend_hash_get_current_key_zval(array, return_value);
1187+
}
11901188
}
11911189
/* }}} */
11921190

ext/standard/tests/array/gh16905.phpt

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
--TEST--
2+
GH-16905 (Internal iterator functions can't handle UNDEF properties)
3+
--FILE--
4+
<?php
5+
6+
class TestSomeUndef {
7+
public int $a;
8+
public int $b;
9+
public int $c;
10+
public int $d;
11+
}
12+
13+
class TestAllUndef {
14+
public int $a;
15+
}
16+
17+
$x = new TestSomeUndef;
18+
$x->b = 1;
19+
$x->c = 2;
20+
21+
var_dump(reset($x));
22+
var_dump(current($x));
23+
var_dump(end($x));
24+
25+
var_dump(reset($x));
26+
var_dump(next($x));
27+
28+
var_dump(end($x));
29+
var_dump(prev($x));
30+
31+
var_dump(key($x));
32+
var_dump(current($x));
33+
34+
$x = new TestAllUndef;
35+
var_dump(key($x));
36+
var_dump(current($x));
37+
38+
$x->a = 1;
39+
var_dump(key($x));
40+
var_dump(current($x));
41+
reset($x);
42+
var_dump(key($x));
43+
var_dump(current($x));
44+
45+
?>
46+
--EXPECTF--
47+
Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d
48+
int(1)
49+
50+
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
51+
int(1)
52+
53+
Deprecated: end(): Calling end() on an object is deprecated in %s on line %d
54+
int(2)
55+
56+
Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d
57+
int(1)
58+
59+
Deprecated: next(): Calling next() on an object is deprecated in %s on line %d
60+
int(2)
61+
62+
Deprecated: end(): Calling end() on an object is deprecated in %s on line %d
63+
int(2)
64+
65+
Deprecated: prev(): Calling prev() on an object is deprecated in %s on line %d
66+
int(1)
67+
68+
Deprecated: key(): Calling key() on an object is deprecated in %s on line %d
69+
string(1) "b"
70+
71+
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
72+
int(1)
73+
74+
Deprecated: key(): Calling key() on an object is deprecated in %s on line %d
75+
NULL
76+
77+
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
78+
bool(false)
79+
80+
Deprecated: key(): Calling key() on an object is deprecated in %s on line %d
81+
NULL
82+
83+
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
84+
bool(false)
85+
86+
Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d
87+
88+
Deprecated: key(): Calling key() on an object is deprecated in %s on line %d
89+
string(1) "a"
90+
91+
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
92+
int(1)

0 commit comments

Comments
 (0)