Поддержание объектных отношений с помощью сериализации PHP - PullRequest
6 голосов
/ 05 августа 2011

Я столкнулся с довольно сложной проблемой, связанной с объектами, реализующими интерфейс Serializable.Давайте рассмотрим пример:

class A {
    public $b;
}

class B {
    public $a;
}

$a = new A;
$b = new B;

$a->b = $b;
$b->a = $a;

echo serialize($a); // O:1:"A":1:{s:1:"b";O:1:"B":1:{s:1:"a";r:1;}}
echo serialize($b); // O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"b";r:1;}}

$a = unserialize(serialize($a));
var_export($a === $a->b->a); // true

В этом примере мы можем видеть, что при использовании встроенной функции сериализации PHP (независимо от того, используем ли мы функцию __sleep()), перекрестные ссылки между A & B сохраняются (r:1;).

Однако, если мы хотим обеспечить использование интерфейса Serializable, возникает проблема:

class A implements Serializable  {
    public $b;

    public function serialize() { return serialize($this->b); }
    public function unserialize($s) { $this->b = unserialize($s); }
}

class B implements Serializable {
    public $a;

    public function serialize() { return serialize($this->a); }
    public function unserialize($s) { $this->a = unserialize($s); }
}

$a = new A;
$b = new B;

$a->b = $b;
$b->a = $a;

echo serialize($a); // infinite loop crashes PHP

Поскольку сериализация каждого объектауправляется независимо, не существует глобального способа увидеть, был ли объект уже сериализован, создать ссылку на него.Затем serialize() функции вызываются в бесконечном цикле.

Есть ли хороший обходной путь для этой проблемы?Шаблон для использования в serialize() функциях?

Ответы [ 3 ]

3 голосов
/ 22 ноября 2012

Эта проблема была названа циклическими ссылками на объекты Проектом Doctrine в их документации по Сериализация объектов :

Сериализуемый не работает хорошо с любымпотенциальные циклические ссылки на объекты (по крайней мере, мы еще не нашли способ, если вы нашли, пожалуйста, свяжитесь с нами).

Так что, насколько я знаю, в настоящее время нет общего решения этой проблемы.

3 голосов
/ 23 ноября 2012

Стандартная сериализация PHP предлагает два вида ссылок, которые работают только на один вызов сериализации последовательным образом: рекурсия и ссылка на рекурсию.

Когда вы реализуете интерфейс Serializable, вы можете растягивать его на несколько объектов и этапов сериализации, пока вы предлагаете один магазин. Когда каждый объект в памяти, например, имеет идентификатор, центральная служба сериализации может быть заполнена ими при сериализации и распакована даже в другом порядке при десериализации.

Служба может позаботиться о том, чтобы выбрать объект с несколькими ссылками на него из хранилища - или, если он уже не сериализован - объект времени выполнения. Поскольку служба является центральной, она может быть легко решена, а методы Serializable могут просто использовать эту службу.

Это также допускает циклические ссылки. Однако вам необходимо реализовать это самостоятельно из-за сопоставления.

Словом, в доктринальном проекте это уже есть. Его форма сериализации объекта - сама база данных. Просто чтобы немного открыть представление, убедитесь, что это не то, что вы ищете.

3 голосов
/ 05 августа 2011

Вот хитрость:

function inStack( $cls, $func ) {
    $backTrace = debug_backtrace();
    for( $i = 2; $i < count( $backTrace ); ++$i  ) {
        if( isset( $backTrace[$i][ 'class' ] ) && $backTrace[$i][ 'class' ] == $cls &&
            isset( $backTrace[$i][ 'function' ] ) && $backTrace[$i][ 'function' ] == $func )
            return true;
    }
    return false;
}

class A implements Serializable  {
    public $b;

    public function serialize() {
        if( inStack( 'A', 'serialize' ) ) return '';
        return serialize( $this->b );
    }
    public function unserialize($s) {
        if( $s == '' ) return null;
        $b = unserialize( $s );
        if( $b !== null ) {
            $this->b = $b;
            $this->b->a = $this;
        }
    }
}

class B implements Serializable {
    public $a;

    public function serialize() {
        if( inStack( 'B', 'serialize' ) ) return '';
        return serialize( $this->a );
    }
    public function unserialize($s) {
        if( $s == '' ) return null;
        $a = unserialize( $s );
        if( $a !== null ) {
            $this->a = $a;
            $this->a->b = $this;
        }
    }
}

$a = new A;
$b = new B;

$a->b = $b;
$b->a = $a;

$a = unserialize( serialize( $a ) );
var_dump( $a === $a->b->a ); //true
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...