在反序列化时选择实例

时间:2013-04-09 16:00:09

标签: php serialization

我正在编写一个类似于Java Currency的PHP类:

  

该类的设计使得任何给定货币永远不会有多个Currency实例。因此,没有公共构造函数。您可以使用getInstance方法获取Currency实例。

使用PHP保存实例化对象的静态数组,并在getInstance()中执行查找以返回现有实例,或者在需要时实例化它是相当容易的。

问题来自序列化。即使我实现了Serializable,我也无法在unserialize()中选择我想要返回的实例,因为此时对象已经被实例化了,因为在PHP中你不能覆盖{{1} }:

$this

是否有任何技术解决方案可以选择在反序列化时返回实例?

- 编辑 -

这是我试图解决的用例:

class Currency implements Serializable
{
    public function getInstance()
    {
        // ...
    }

    public function serialize()
    {
        // ...
    }

    public function unserialize($data)
    {
        // At this point, the object is already instantiated,
        // so I can't just return self::getInstance(),
        // and can't override $this
    }
}

我知道我可以使用$euro = Currency::getInstance('EUR'); assert($euro === unserialize(serialize($euro)); 构建一个类似的对象,但我想知道是否有可能获得相同的对象。

1 个答案:

答案 0 :(得分:1)

td; dr; 不,你不能在PHP中本地执行此操作,因为unserialize()不支持与Java相同的readResolve()功能。

在Java中,在反序列化时,会创建一个对象的新实例,恢复其状态,然后在类上调用readResolve()(如果存在)。 readResolve()获取新创建的反序列化对象,然后将其解析为另一个对象(如果需要),此时解析的对象是反序列化返回的对象。如果readResolve()返回与提供给它的对象不同的对象,则最初创建的新实例将被废弃。

在PHP中,unserialize()内没有这样的钩子。您可以实施变通方法,但是,定义您自己的解析方法以模拟readResolve(),但您需要在unserialize()之后调用它。

class Currency implements Serializable {
    private $currencyCode;
    private static $instances = array();

    private function __construct( $currencyCode) {
        $this->currencyCode = $currencyCode;
    }

    public static function getInstance( $currencyCode) {
        if( !isset( self::$instances[$currencyCode])) {
            self::$instances[$currencyCode] = new Currency( $currencyCode);
        }
        return self::$instances[$currencyCode];
    }

    public function serialize() {
        return serialize( $this->currencyCode);
    }

    public function unserialize( $data) {
        $this->currencyCode = unserialize( $data);
    }

    public static function resolve( $obj) {
        $new = self::getInstance( $obj->currencyCode);
        if( $new !== $obj) {
            unset( $obj);
        }
        return $new;
    }
}

$euro = Currency::getInstance('EUR');
assert($euro === Currency::resolve( unserialize(serialize($euro))));

当然,没有什么可以阻止某人不要调用Currency::resolve(),然后你会得到多个给定货币代码的对象。