varuog
varuog

Reputation: 3081

PHP Infinite recursion when serializing object with Dependency Injection

class foo
{
 private $deps

 public function __construct(bar $obj)
 {
  $this->deps=$obj;
 }
}


class Bar
{
  private $property; //Instance of Bar which have setter and getter methods

}

Both implements an interface which serialize the objects to XML. But it is get into infinite recurrsion Due to cross refference. How to resolve this? Is this my design fault? How to improve it?

One way i can think of last time cleanup of dependency before serializing. But i dont know it will be best way or not.

Upvotes: 1

Views: 1480

Answers (1)

Gordon
Gordon

Reputation: 317119

When your Serializer simply traverses the object graph, you will inevitably run into infinite recursion when you have bi-directional associations, e.g. Article has and belongs to many Comments. The Serializer will traverse back and forth between the Article and Comments then. It's not so much about dependencies than it is about how you associate and link objects with each other.

I can think of three ways to approach this problem:

Object tracking

The easiest solution would probably be to teach your Serializer to record which objects it serialized already. To do so, run each object to be serialized through spl_object_hash before serializing it. Store that hash in an array and when the hash is already in the recorded list, then just skip it or insert an element with an idref attribute pointing to the already inserted element, e.g. something like

class Serializer
{
    private $record = array();

    public function serialize($value)
    {
        $hash = spl_object_hash($value);
        if (isset($this->record[$hash])) {
            // skip or insert element with idref to XML
        } else {
            $this->record[$hash] = true;
            // turn $value to XML
        }
    }

// … more code

Metadata Mapping pattern

Another option would be to utilize an equivalent of the MetadataMapping pattern for Serializing:

Much of the code that deals with object-relational mapping describes how fields in the database correspond to fields in in-memory objects. The resulting code tends to be tedious and repetitive to write. A Metadata Mapping allows developers to define the mappings in a simple tabular form, which can then be processed by generic code to carry out the details of reading, inserting, and updating the data.

Instead of defining which properties match to which columns in the database, you define which properties should get serialized to XML. Your Serializer would then inspect the type of the object and apply the rules from the Mapping to handle it. Obviously, your map should not contain any properties that could lead to a circular reference then.

The Mapping could be as simple as an array which you can then pass to the Serializer to configure it. This way, neither your objects, nor your Serializer needs to know any details of how the Mapping happens. The drawback is, you need to define the Mappings.

Visitor pattern

Yet another option would be to use a Visitor pattern, where the Serializer visits the object graph and the objects then selectively pass the data to be serialized in a Double Dispatch. Again, you'd have to make sure to only pass data that cannot lead to the circular reference. The drawback is that it requires all the involved classes to accept the Visitor.

I won't go into details on this pattern since it has been covered extensively on the web.

Upvotes: 2

Related Questions