我正在编写一个类似于Java货币的PHP类:
该类的设计使得任何给定的货币永远不会有一个以上的Currency实例。因此,没有公共构造函数。您可以使用getInstance方法获得Currency实例。
使用PHP保持一个实例化对象的静态数组,并在getInstance()
中执行查找以返回一个现有实例,或者在需要时实例化它,这是相当容易的。
问题来自于序列化。即使我实现Serializable,我也没有办法选择我想在unserialize()
中返回的实例,因为这个对象在此时已经实例化了,而且因为在PHP中你不能重写$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
}
}
是否有任何技术解决方案来选择在反序列化时返回的实例?
—edit—
这是我试图解决的用例:
$euro = Currency::getInstance('EUR');
assert($euro === unserialize(serialize($euro));
我知道我可以用unserialize()
建立一个类似的对象,但我想知道是否有可能获得相同的对象。
td;dr;不,您不能在PHP中本地做到这一点,因为unserialize()
不支持与Java相同的readResolve()
功能。
在Java中,在反序列化时,创建对象的新实例,恢复其状态,然后在类上调用readResolve()
(如果它存在)。readResolve()
接受新创建的反序列化对象,然后将其解析为另一个对象(如果需要),此时解析后的对象就是反序列化返回的对象。如果readResolve()
返回的对象与提供给它的对象不同,那么最初创建的新实例将被垃圾收集。
在PHP中,unserialize()
中没有这样的钩子。但是,您可以实现一个变通方法,定义您自己的resolve方法来模拟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()
,然后您将最终获得多个给定货币代码的对象。