4

I have this example array.

$data = new stdClass();

$data->foo = [
    'foo1' => &$data,
    'foo2' => 23,
];

$data->bar = new stdClass();

$data->nar->object = [
    'bar1' => &$data->bar,
    'bar2' => 43,
];

I want to parse this to:

$data = new stdClass();

$data->foo = [
    'foo1' => "RECURSION DETECTED",
    'foo2' => 23,
];

$data->bar = new stdClass();

$data->nar->object = [
    'bar1' => "RECURSION DETECTED",
    'bar2' => 43,
];

I need it, because json_encode can't encode data when recursion is detected.

I tried so many times and in different ways, I did a lot of research, but I did not find anything to really help me.

My last attempt was:

function _stack(&$object, &$stack = [], $key = 'original')
{
    if (isObjectOrArray($object)) {
        if (!in_array($object, $stack, true)) {
            if (is_object($object)) {
                $stack[$key] = &$object;
            }
            foreach ($object as $key => &$value) {
                _stack($value, $stack, $key);
            }
        }
    }
    return $stack;
}

function _remove($object, $stack, $objectO = false, $key = 'original')
{
    /**
     * @var $objectO false | object
     */
    if (!$objectO) {
        $objectO = $object;
    }
    if (isObjectOrArray($object)) {
        foreach ($object as $prop => $value) {
            if (is_object($objectO)) {
                if (in_array($object->{$prop}, $stack, true) && $prop !== $key) {
                    $objectO->{$prop} = "RECURSION DETECTED";
                } else {
                    $objectO->{$prop} = _remove($object->{$prop}, $stack, $objectO->{$prop}, $prop);
                }
            } else {
                if (in_array($object[$prop], $stack, true) && $prop !== $key) {
                    $objectO[$prop] = "RECURSION DETECTED";
                } else {
                    $objectO[$prop] = _remove($object[$prop], $stack, $objectO[$prop], $prop);
                }
            }
        }
    }
    return $objectO;
}

First i crate an stack with original objects (not reference / pointer). The key is passed to the function, within itself in recursion, so I know exactly where recursion meets the original object. I need it so I can then tell what the pointer is and what the original object is.

After create stack i run the same looping, but the current value inside foreach statement is an object and he is inside stack and the current key is diferent of current key pass to the function call, the reference / pointer is breaked.

Array
(
    [foo1] => RECURSION DETECTED
    [foo2] => 23
)

But at the end of all function calls I get only:

RECURSION DETECTED

1 Answer 1

5

I am still looking at another way since this is interesting, but it is easy to replace the reference pointer in a serialized string and then unserialize it:

$data = unserialize(preg_replace('/R:\d+/', 's:18:"RECURSION DETECTED"', serialize($data)));

Another option for PHP >= 7.3.0 is exporting and forcing it to break the references. var_export will complain about recursion, however it will happily display it with the references replaced with NULL. var_export has a second argument to return the output instead of displaying, but this doesn't work with recursion so I buffered and captured the output.

ob_start();
@var_export($data);
$var = ob_get_clean();
eval("\$data = $var;");

For PHP < 7.3.0 you can use the above code with your own class that implements __set_state instead of stdClass:

class myClass {

    public static function __set_state($array) {
        $o = new self;

        foreach($array as $key => $val) {
            $o->$key = $val;
        }
        return $o;
    }
}

$data = new myClass();
Sign up to request clarification or add additional context in comments.

4 Comments

Yeap, it's works and this is very simple. I need replace R to r.
$data = unserialize(preg_replace('/R:\d+/', 's:18:"RECURSION DETECTED"', serialize($data))) works perfect, however, what's the point in the preg_replace, as unserialize(serialize($data)) works too?
Just in case the pattern R:\d+ occurs inside a string within the data, you can add context to make it more specific: these reference markers will always follow ; or { and be followed by ; (I think that's all), so: $data = unserialize(preg_replace('/(;|\{)R:\d+;/', '$1s:18:"RECURSION DETECTED";', serialize($data)));
Correction to above: only ; can precede the marker, so the pattern/replacement stays more simple: $data = unserialize(preg_replace('/;R:\d+;/', ';s:18:"RECURSION DETECTED";', serialize($data)));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.