[go: up one dir, main page]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array Data Address Reference Issue #10519

Closed
uuur86 opened this issue Feb 6, 2023 · 1 comment
Closed

Array Data Address Reference Issue #10519

uuur86 opened this issue Feb 6, 2023 · 1 comment

Comments

@uuur86
Copy link
uuur86 commented Feb 6, 2023

Description

When I try to create a new record inside a RecursiveArrayIterator Object using offsetSet method, the previously specified address reference is cancelled and I lose the connection with the object.

The following code:

<?php
use Iterator;
use Traversable;
use RecursiveArrayIterator;
use JsonSerializable;

use RecursiveIterator;

interface DataInterface extends JsonSerializable, RecursiveIterator, Iterator
{
    /**
     * @param mixed $data
     *
     * @return DataInterface
     */
    public static function init(Traversable $data): DataInterface;
}


class A extends RecursiveArrayIterator implements DataInterface
{
    public function __construct($data)
    {
        parent::__construct($data);
    }
    
    public static function init($data): DataInterface
    {
        return new static($data);
    }
    
    public function getCols(string $colname): array
    {
        return array_column($this->getArrayCopy(), $colname);
    }
    
    public function bugySetMethod($key, $value)
    {
        $data = &$this;

        while ($data->hasChildren()) {
            $data = $data->getChildren();
        }
        
        $data->offsetSet($key, $value);
    }
    
    public function jsonSerialize()
    {
        return $this;
    }
}

$example = A::init([
    'test' => [
        'a' => (object)[2 => '',3 => '',4 => ''],
    ]
]);

$example->bugySetMethod(5, 'in here');
var_dump(json_encode($example));

// result
// string(51) "{"test":{"a":{"2":"","3":"","4":"","5":"in here"}}}"

$example = A::init([
    'test' => [
        'b' => [2 => '',3 => '',4 => ''],
    ]
]);

$example->bugySetMethod(5, 'must be here');
var_dump(json_encode($example));

// result
// string(37) "{"test":{"b":{"2":"","3":"","4":""}}}"

Resulted in this output:

string(37) "{"test":{"b":{"2":"","3":"","4":""}}}"

But I expected this output instead:

string(37) "{"test":{"b":{"2":"","3":"","4":"","5":"must be here"}}}"

PHP Version

Operating System

@Girgias
Copy link
Member
Girgias commented Feb 6, 2023

I think I've figured out the issue:

The default behaviour of RecursiveArrayIterator::getChildren()

php-src/ext/spl/spl_array.c

Lines 1487 to 1498 in c8ec2ed

ZVAL_DEREF(entry);
if (Z_TYPE_P(entry) == IS_OBJECT) {
if ((intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) != 0) {
RETURN_NULL();
}
if (instanceof_function(Z_OBJCE_P(entry), Z_OBJCE_P(ZEND_THIS))) {
RETURN_OBJ_COPY(Z_OBJ_P(entry));
}
}
ZVAL_LONG(&flags, intern->ar_flags);
spl_instantiate_arg_ex2(Z_OBJCE_P(ZEND_THIS), return_value, entry, &flags);
is to return a new object for arrays.

Not sure exactly how to fix this but replacing the array entry with an instance of ArrayIterator might be sensible? @cmb69 what's your opinion?

NathanFreeman added a commit to NathanFreeman/php-src that referenced this issue Mar 2, 2023
Girgias added a commit that referenced this issue Mar 10, 2023
* PHP-8.1:
  Fix GH-10519: Array Data Address Reference Issue
Girgias added a commit that referenced this issue Mar 10, 2023
* PHP-8.2:
  Fix GH-10519: Array Data Address Reference Issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants
@Girgias @uuur86 and others