Closed
Description
Description
I believe that a lot of code in php-src/pecls relies on the fact that the pointer of Z_OBJPROP_P doesn't change, for non-empty properties tables. There's may be more examples than just array_walk but I'm still trying to create one for var_export
- The issues affect 52ae641 and newer after the fix to var_export emits debug assertion errors for
HT_ASSERT_RC1(ht);
in builds for SplFixedArray circular references #8044 - Because the properties table was getting cloned, I assume the iterator is getting affected.
- Z_OBJPROP_P calls get_properties, which now replaces the original properties table with a clone and returns the original property table to the caller.
- array_walk's implementation calls Z_OBJPROP_P in a loop.
The following code:
<?php
call_user_func(function () {
$x = new SplFixedArray(2);
$x[0] = $x;
$x[1] = strtoupper('foo');
echo "Calling array_walk\n";
array_walk($x, function ($value, $index) use($x) {
printf("Value=%s index=%s\n", json_encode($value), json_encode($index));
echo "Calling json_encode\n";
echo json_encode($x, JSON_PARTIAL_OUTPUT_ON_ERROR), "\n";
});
});
Resulted in this output:
// PHP hangs in an infinite loop.
// Adding debug statements to spl_fixedarray_object_get_properties indicates it is repeatedly called and cloning the array in 52ae6417ca3e62eff796bf3e3b5662fe6a9ee085
But I expected this output instead:
Calling array_walk
Value= index=0
Calling json_encode
[null,"FOO"]
Value="FOO" index=1
Calling json_encode
[null,"FOO"]
Relevant parts of the code - zend_hash_iterator_pos probably resets to the start if Z_OBJPROP is a different ht from when starting.
// Zend/zend_hash.c
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos(uint32_t idx, HashTable *ht)
{
HashTableIterator *iter = EG(ht_iterators) + idx;
ZEND_ASSERT(idx != (uint32_t)-1);
if (UNEXPECTED(iter->ht != ht)) {
// ext/standard/array.c
static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
// ...
} else if (Z_TYPE_P(array) == IS_OBJECT) {
target_hash = Z_OBJPROP_P(array);
pos = zend_hash_iterator_pos(ht_iter, target_hash);
PHP Version
PHP 8.0.17-dev(52ae641 or newer)
Operating System
No response