2010-08-27 3 views
5

j'ai mis en place un modèle simple composite en utilisant SplObjectStorage, comme dans l'exemple ci-dessus:Erreur sérialisation un arbre d'objets avec 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); 
    } 
} 

Chaque fois que je tente de sérialiser un objet composite, PHP 5.3.2 me jette un Segmentation Fault. Cela se produit uniquement lorsque j'ajoute un nombre quelconque de nœuds de n'importe quel type à l'objet.

C'est le code incriminé:

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

Bien que celui-ci fonctionne:

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

De plus, si je mets en œuvre le modèle composite avec array() au lieu de SplObjectStorage, tout fonctionne bien aussi.

Ce que je fais mal? En réglant le Parent, vous avez une référence circulaire.

Répondre

8

PHP va essayer de sérialiser le composite, tous ses nœuds et les nœuds vont à leur tour essayer de sérialiser le composite .. boom!

Vous pouvez utiliser les méthodes magiques __sleep and __wakeup() pour supprimer (ou faire quoi que ce soit) de la référence parente lors de la sérialisation.

EDIT:

Voir si les ajouter à Composite résout le problème:

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

... et la méthode __wakeup en composite pour restaurer la référence parent en appelant setParent ($ this-) sur chaque élément enfant. – VolkerK

+1

Merci! Je pensais que serialize() serait assez intelligent pour gérer les références, mais il ne l'est pas. Je l'ai résolu en implémentant l'interface Serializable dans les deux classes. – xPheRe