Merge branch 'PHP-8.4'

* PHP-8.4:
  Fix GH-16905: Internal iterator functions can't handle UNDEF properties
This commit is contained in:
Niels Dossche 2024-11-28 19:23:02 +01:00
commit c10f5ce9c1
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
2 changed files with 141 additions and 51 deletions

View file

@ -1024,11 +1024,50 @@ static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
return zobj->handlers->get_properties(zobj); return zobj->handlers->get_properties(zobj);
} }
static zval *php_array_iter_seek_current(HashTable *array, bool forward_direction)
{
zval *entry;
while (true) {
if ((entry = zend_hash_get_current_data(array)) == NULL) {
return NULL;
}
ZVAL_DEINDIRECT(entry);
/* Possible with an uninitialized typed property */
if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
zend_result result;
if (forward_direction) {
result = zend_hash_move_forward(array);
} else {
result = zend_hash_move_backwards(array);
}
if (result != SUCCESS) {
return NULL;
}
} else {
break;
}
}
return entry;
}
static void php_array_iter_return_current(zval *return_value, HashTable *array, bool forward_direction)
{
zval *entry = php_array_iter_seek_current(array, forward_direction);
if (EXPECTED(entry)) {
RETURN_COPY_DEREF(entry);
} else {
RETURN_FALSE;
}
}
/* {{{ Advances array argument's internal pointer to the last element and return it */ /* {{{ Advances array argument's internal pointer to the last element and return it */
PHP_FUNCTION(end) PHP_FUNCTION(end)
{ {
zval *array_zv; zval *array_zv;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1) ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@ -1042,15 +1081,7 @@ PHP_FUNCTION(end)
zend_hash_internal_pointer_end(array); zend_hash_internal_pointer_end(array);
if (USED_RET()) { if (USED_RET()) {
if ((entry = zend_hash_get_current_data(array)) == NULL) { php_array_iter_return_current(return_value, array, false);
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
RETURN_COPY_DEREF(entry);
} }
} }
/* }}} */ /* }}} */
@ -1059,7 +1090,6 @@ PHP_FUNCTION(end)
PHP_FUNCTION(prev) PHP_FUNCTION(prev)
{ {
zval *array_zv; zval *array_zv;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1) ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@ -1073,15 +1103,7 @@ PHP_FUNCTION(prev)
zend_hash_move_backwards(array); zend_hash_move_backwards(array);
if (USED_RET()) { if (USED_RET()) {
if ((entry = zend_hash_get_current_data(array)) == NULL) { php_array_iter_return_current(return_value, array, false);
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
RETURN_COPY_DEREF(entry);
} }
} }
/* }}} */ /* }}} */
@ -1090,7 +1112,6 @@ PHP_FUNCTION(prev)
PHP_FUNCTION(next) PHP_FUNCTION(next)
{ {
zval *array_zv; zval *array_zv;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1) ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@ -1104,15 +1125,7 @@ PHP_FUNCTION(next)
zend_hash_move_forward(array); zend_hash_move_forward(array);
if (USED_RET()) { if (USED_RET()) {
if ((entry = zend_hash_get_current_data(array)) == NULL) { php_array_iter_return_current(return_value, array, true);
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
RETURN_COPY_DEREF(entry);
} }
} }
/* }}} */ /* }}} */
@ -1121,7 +1134,6 @@ PHP_FUNCTION(next)
PHP_FUNCTION(reset) PHP_FUNCTION(reset)
{ {
zval *array_zv; zval *array_zv;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1) ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
@ -1135,15 +1147,7 @@ PHP_FUNCTION(reset)
zend_hash_internal_pointer_reset(array); zend_hash_internal_pointer_reset(array);
if (USED_RET()) { if (USED_RET()) {
if ((entry = zend_hash_get_current_data(array)) == NULL) { php_array_iter_return_current(return_value, array, true);
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
RETURN_COPY_DEREF(entry);
} }
} }
/* }}} */ /* }}} */
@ -1152,22 +1156,13 @@ PHP_FUNCTION(reset)
PHP_FUNCTION(current) PHP_FUNCTION(current)
{ {
zval *array_zv; zval *array_zv;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1) ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT(array_zv) Z_PARAM_ARRAY_OR_OBJECT(array_zv)
ZEND_PARSE_PARAMETERS_END(); ZEND_PARSE_PARAMETERS_END();
HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
if ((entry = zend_hash_get_current_data(array)) == NULL) { php_array_iter_return_current(return_value, array, true);
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
RETURN_COPY_DEREF(entry);
} }
/* }}} */ /* }}} */
@ -1181,8 +1176,11 @@ PHP_FUNCTION(key)
ZEND_PARSE_PARAMETERS_END(); ZEND_PARSE_PARAMETERS_END();
HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
zval *entry = php_array_iter_seek_current(array, true);
if (EXPECTED(entry)) {
zend_hash_get_current_key_zval(array, return_value); zend_hash_get_current_key_zval(array, return_value);
} }
}
/* }}} */ /* }}} */
static int php_data_compare(const void *f, const void *s) /* {{{ */ static int php_data_compare(const void *f, const void *s) /* {{{ */

View file

@ -0,0 +1,92 @@
--TEST--
GH-16905 (Internal iterator functions can't handle UNDEF properties)
--FILE--
<?php
class TestSomeUndef {
public int $a;
public int $b;
public int $c;
public int $d;
}
class TestAllUndef {
public int $a;
}
$x = new TestSomeUndef;
$x->b = 1;
$x->c = 2;
var_dump(reset($x));
var_dump(current($x));
var_dump(end($x));
var_dump(reset($x));
var_dump(next($x));
var_dump(end($x));
var_dump(prev($x));
var_dump(key($x));
var_dump(current($x));
$x = new TestAllUndef;
var_dump(key($x));
var_dump(current($x));
$x->a = 1;
var_dump(key($x));
var_dump(current($x));
reset($x);
var_dump(key($x));
var_dump(current($x));
?>
--EXPECTF--
Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d
int(1)
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
int(1)
Deprecated: end(): Calling end() on an object is deprecated in %s on line %d
int(2)
Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d
int(1)
Deprecated: next(): Calling next() on an object is deprecated in %s on line %d
int(2)
Deprecated: end(): Calling end() on an object is deprecated in %s on line %d
int(2)
Deprecated: prev(): Calling prev() on an object is deprecated in %s on line %d
int(1)
Deprecated: key(): Calling key() on an object is deprecated in %s on line %d
string(1) "b"
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
int(1)
Deprecated: key(): Calling key() on an object is deprecated in %s on line %d
NULL
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
bool(false)
Deprecated: key(): Calling key() on an object is deprecated in %s on line %d
NULL
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
bool(false)
Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d
Deprecated: key(): Calling key() on an object is deprecated in %s on line %d
string(1) "a"
Deprecated: current(): Calling current() on an object is deprecated in %s on line %d
int(1)