使用依赖注入序列化对象时的PHP无限递归

时间:2013-06-21 00:52:40

标签: php oop dependency-injection xml-serialization

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

}

两者都实现了一个将对象序列化为XML的接口。但由于交叉引用,它会进入无限复发。怎么解决这个?这是我的设计错吗?如何改进?

我可以想到上次在序列化之前清理依赖关系的一种方法。但我不知道这将是最好的方式。

1 个答案:

答案 0 :(得分:2)

当你的Serializer只是遍历对象图时,当你有双向关联时,你将不可避免地遇到无限递归,例如:文章有并且属于许多评论。然后,序列化程序将在文章和评论之间来回移动。它不是关于依赖关系,而是关于如何关联和链接对象。

我可以想到三种方法来解决这个问题:

对象跟踪

最简单的解决方案可能是教你的Serializer记录它已经序列化的对象。为此,请在序列化之前运行要通过spl_object_hash序列化的每个对象。将该哈希存储在数组中,并且当哈希已经在记录列表中时,则只需跳过它或插入具有指向已插入元素的idref属性的元素,例如,

之类的东西
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

元数据映射模式

另一个选择是使用等效的MetadataMapping模式进行序列化:

  

处理对象关系映射的大部分代码描述了数据库中的字段如何与内存中对象中的字段相对应。由此产生的代码往往是乏味和重复写。元数据映射允许开发人员以简单的表格形式定义映射,然后可以通过通用代码处理映射,以执行读取,插入和更新数据的详细信息。

您可以定义哪些属性应序列化为XML,而不是定义哪些属性与数据库中的哪些列匹配。然后,您的Serializer将检查对象的类型,并应用Mapping中的规则来处理它。显然,您的地图不应包含任何可能导致循环引用的属性。

映射可以像一个数组一样简单,然后您可以将其传递给Serializer进行配置。这样,您的对象和Serializer都不需要知道映射如何发生的任何细节。缺点是,您需要定义映射。

访客模式

另一种选择是使用Visitor pattern,其中Serializer访问对象图,然后对象选择性地传递要在Double Dispatch中序列化的数据。同样,您必须确保仅传递无法导致循环引用的数据。缺点是它需要所有涉及的类接受访问者。

我不会详细介绍这种模式,因为它已在网上广泛报道。