2010-08-27 3 views
5

я реализовал простой композитный образец с использованием SplObjectStorage, как в примере выше:Ошибка сериализация дерева объектов с SplObjectStorage

class Node 
{ 
    private $parent = null; 

    public function setParent(Composite $parent) 
    { 
     $this->parent = $parent; 
    } 
} 

class Composite extends Node 
{ 
    private $children; 

    public function __construct() 
    { 
     $this->children = new SplObjectStorage; 
    } 

    public function add(Node $node) 
    { 
     $this->children->attach($node); 
     $node->setParent($this); 
    } 
} 

Всякий раз, когда я пытаюсь сериализации композитного объекта, PHP 5.3.2 бросает мне Segmentation Fault. Это происходит только тогда, когда я добавляю к объекту любое количество узлов любого типа.

Это код обижая:

$node = new Node; 
$composite = new Composite; 
$composite->add($node); 
echo serialize($composite); 

Хотя это работает:

$node = new Node; 
$composite = new Composite; 
echo serialize($composite); 

Кроме того, если я реализую композитный шаблон с массивом() вместо SplObjectStorage, все бежит слишком хорошо.

Что я делаю неправильно?

ответ

8

Установив родителя, вы имеете круглую ссылку. PHP попытается сериализовать композит, все его узлы и узлы, в свою очередь, попытаются сериализовать композитный .. бум!

Вы можете использовать методы магии __sleep and __wakeup(), чтобы удалить (или сделать что-либо) исходную ссылку при сериализации.

EDIT:

Смотрите, если при добавлении их к Composite исправления вопрос:

public function __sleep() 
{ 
    $this->children = iterator_to_array($this->children); 
    return array('parent', 'children'); 
} 
public function __wakeup() 
{ 
    $storage = new SplObjectStorage; 
    array_map(array($storage, 'attach'), $this->children); 
    $this->children = $storage; 
} 
+1

... и метод __wakeup в Composite восстановить родительскую ссылку по телефону SetParent ($ это) на каждый дочерний элемент. – VolkerK

+1

Спасибо! Я думал, что serialize() будет достаточно умен, чтобы справляться со ссылками, но это не так. Я решил это, реализовав интерфейс Serializable в обоих классах. – xPheRe