Skip to content

Commit c10f5ce

Browse files
committed
Merge branch 'PHP-8.4'
* PHP-8.4: Fix GH-16905: Internal iterator functions can't handle UNDEF properties
2 parents f4289ab + 3a80936 commit c10f5ce

File tree

2 files changed

+141
-51
lines changed

2 files changed

+141
-51
lines changed

ext/standard/array.c

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

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

10331072
ZEND_PARSE_PARAMETERS_START(1, 1)
10341073
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@@ -1042,15 +1081,7 @@ PHP_FUNCTION(end)
10421081
zend_hash_internal_pointer_end(array);
10431082

10441083
if (USED_RET()) {
1045-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1046-
RETURN_FALSE;
1047-
}
1048-
1049-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1050-
entry = Z_INDIRECT_P(entry);
1051-
}
1052-
1053-
RETURN_COPY_DEREF(entry);
1084+
php_array_iter_return_current(return_value, array, false);
10541085
}
10551086
}
10561087
/* }}} */
@@ -1059,7 +1090,6 @@ PHP_FUNCTION(end)
10591090
PHP_FUNCTION(prev)
10601091
{
10611092
zval *array_zv;
1062-
zval *entry;
10631093

10641094
ZEND_PARSE_PARAMETERS_START(1, 1)
10651095
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@@ -1073,15 +1103,7 @@ PHP_FUNCTION(prev)
10731103
zend_hash_move_backwards(array);
10741104

10751105
if (USED_RET()) {
1076-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1077-
RETURN_FALSE;
1078-
}
1079-
1080-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1081-
entry = Z_INDIRECT_P(entry);
1082-
}
1083-
1084-
RETURN_COPY_DEREF(entry);
1106+
php_array_iter_return_current(return_value, array, false);
10851107
}
10861108
}
10871109
/* }}} */
@@ -1090,7 +1112,6 @@ PHP_FUNCTION(prev)
10901112
PHP_FUNCTION(next)
10911113
{
10921114
zval *array_zv;
1093-
zval *entry;
10941115

10951116
ZEND_PARSE_PARAMETERS_START(1, 1)
10961117
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@@ -1104,15 +1125,7 @@ PHP_FUNCTION(next)
11041125
zend_hash_move_forward(array);
11051126

11061127
if (USED_RET()) {
1107-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1108-
RETURN_FALSE;
1109-
}
1110-
1111-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1112-
entry = Z_INDIRECT_P(entry);
1113-
}
1114-
1115-
RETURN_COPY_DEREF(entry);
1128+
php_array_iter_return_current(return_value, array, true);
11161129
}
11171130
}
11181131
/* }}} */
@@ -1121,7 +1134,6 @@ PHP_FUNCTION(next)
11211134
PHP_FUNCTION(reset)
11221135
{
11231136
zval *array_zv;
1124-
zval *entry;
11251137

11261138
ZEND_PARSE_PARAMETERS_START(1, 1)
11271139
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@@ -1135,15 +1147,7 @@ PHP_FUNCTION(reset)
11351147
zend_hash_internal_pointer_reset(array);
11361148

11371149
if (USED_RET()) {
1138-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1139-
RETURN_FALSE;
1140-
}
1141-
1142-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1143-
entry = Z_INDIRECT_P(entry);
1144-
}
1145-
1146-
RETURN_COPY_DEREF(entry);
1150+
php_array_iter_return_current(return_value, array, true);
11471151
}
11481152
}
11491153
/* }}} */
@@ -1152,22 +1156,13 @@ PHP_FUNCTION(reset)
11521156
PHP_FUNCTION(current)
11531157
{
11541158
zval *array_zv;
1155-
zval *entry;
11561159

11571160
ZEND_PARSE_PARAMETERS_START(1, 1)
11581161
Z_PARAM_ARRAY_OR_OBJECT(array_zv)
11591162
ZEND_PARSE_PARAMETERS_END();
11601163

11611164
HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1162-
if ((entry = zend_hash_get_current_data(array)) == NULL) {
1163-
RETURN_FALSE;
1164-
}
1165-
1166-
if (Z_TYPE_P(entry) == IS_INDIRECT) {
1167-
entry = Z_INDIRECT_P(entry);
1168-
}
1169-
1170-
RETURN_COPY_DEREF(entry);
1165+
php_array_iter_return_current(return_value, array, true);
11711166
}
11721167
/* }}} */
11731168

@@ -1181,7 +1176,10 @@ PHP_FUNCTION(key)
11811176
ZEND_PARSE_PARAMETERS_END();
11821177

11831178
HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1184-
zend_hash_get_current_key_zval(array, return_value);
1179+
zval *entry = php_array_iter_seek_current(array, true);
1180+
if (EXPECTED(entry)) {
1181+
zend_hash_get_current_key_zval(array, return_value);
1182+
}
11851183
}
11861184
/* }}} */
11871185

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)