Как удалить рекурсию внутри объекта или массива? - PullRequest
3 голосов
/ 08 марта 2019

У меня есть этот пример массива.

$data = new stdClass();

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

$data->bar = new stdClass();

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

Я хочу разобрать это:

$data = new stdClass();

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

$data->bar = new stdClass();

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

Мне это нужно, потому что json_encode не может кодировать данные при обнаружении рекурсии.

Я пробовал так много раз и по-разному, я провел много исследований, но я не нашел ничего, что действительно помогло бы мне.

Моя последняя попытка была:

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;
}

Сначала я создаю стопку с оригинальными объектами (не указатель / указатель). Ключ передается функции внутри самой рекурсии, поэтому я точно знаю, где рекурсия встречается с исходным объектом. Мне это нужно, чтобы я мог потом сказать, что это за указатель и каков исходный объект.

После создания стека я запускаю ту же петлю, но текущее значение внутри оператора foreach является объектом, и он находится внутри стека, а текущий ключ отличается от текущей передачи ключа на вызов функции, ссылка / указатель прерывается.

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

Но в конце всех вызовов функций я получаю только:

RECURSION DETECTED

1 Ответ

3 голосов
/ 08 марта 2019

Я все еще смотрю на другой путь, так как это интересно, но легко заменить ссылочный указатель в сериализованной строке и затем десериализовать его:

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

Другая опция для PHP> = 7.3.0 экспортирует и заставляет его ломать ссылки.var_export будет жаловаться на рекурсию, однако с радостью отобразит ее со ссылками, замененными на NULL.var_export имеет второй аргумент, чтобы вернуть вывод вместо отображения, но это не работает с рекурсией, поэтому я буферизовал и захватил вывод.

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

Для PHP <7.3.0 вы можете использоватькод выше с вашим собственным классом, который реализует <code>__set_state вместо 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...