带有内部值引用的序列化对象

时间:2015-07-08 21:21:10

标签: php oop object serialization reference

这是我在PHP中看到的最奇怪的东西,但肯定有某种解释。

使用serialize()我正在存储一些对象。稍后,我使用unserialize()恢复它们。

今天我发现了一个已被反序列化的对象的问题。想象一下这种情况:

object__product_bundle Object (
    [collateralValue] => 
    [collateralGroup] =>
)

现在假设$objobject__product_bundle的一个实例,如上所示。

当我做的时候:

$obj->collateralValue = 10;

并检查了对象变量,我看到了:

object__product_bundle Object (
    [collateralValue] => 10
    [collateralGroup] => 10
)

Mindboggling!

我花了一个小时把头撞在桌子上,因为这没有意义。但是当我开始在对象上使用var_dump()时,在对其进行更改之前,我看到了这一点:

object(object__product_bundle)#28 (15) {
    ["collateralValue"] => &NULL
    ["collateralGroup"] => &NULL
}

显然这些属性/变量以某种方式相关联。我研究了&NULL,我找到的只是this question,它告诉我我正在处理某种参考文献。

但是怎么样?

我的对象来自序列化字符串。

现在,看一下我发现的序列化字符串:

s:15:"collateralValue";N;s:15:"collateralGroup";R:15;

什么是R:15?

这可能是问题吗?

如何解决这个问题以及它来自哪里?

修改

深入挖掘后,我找到了罪魁祸首。

Orientiation:

对象(如上所述)存储在另一个对象的属性中,该对象是商店购物车的项目。

class shopCart {
    public $storage;
}

$cart->storage[] = new shopCart_item();

class shopCart_item {
    public $object;
}

$object是存储产品(object__product_*)的地方。

下订单时,为了重复(订阅),整个shopCart将作为blob存储到数据库中。

每当订阅订阅订单时,自动化任务就会抓取旧版shopCart并从中生成新订单。

在这里我找到了罪魁祸首 - 我在开发过程中稍后添加了属性(collateralValue等),但是已经存储了订单。

现在在调试期间我发现这是PHP开始创建引用的地方,虽然我不明白为什么。

简单地说:

static public function generateOrderFromSubscription() {
    [...]
    $order = new object__webShop_order();
    var_dump($subscription->cart); // <-- no references are in here at all
    $order->cart = serialize($subscription->cart);
    var_dump($order->cart); // <-- suddenly, here i have the references
}

Apparantely,我为每个__sleep()使用object__product_* - 它返回这些变量名称(包括collateralValue等等)。

现在的问题是:为什么PHP在处理睡眠对象的新属性时会创建引用,但同时其结构已发生变化?

非常混乱!

编辑#2

最后有些希望。

我的__sleep()函数基本上返回了一个硬编码的变量名称数组,因为还有很多其他我从未希望存储在数据库中的函数。这种方法显然导致了这个问题中描述的当前问题。

我仍然不知道为什么PHP会为没有这些变量而被唤醒的对象中的变量创建引用,但这些变量在__sleep()中返回。

对我来说唯一明智的解决方案似乎是要适应__sleep()。我现在这样做:

public function __sleep(){

    $vars=array(
        'dbId',
        'title',
        'articleId',
        'price_per_unit',
    );

    if(isset($this->collateralValue))
        $vars[]='collateralValue';
    if(isset($this->collateralGroup))
        $vars[]='collateralGroup';

}

这样,__sleep()将不会返回(这两个新的)变量名称(collat​​eralValue,collat​​eralGroup),这些名称在当前对象中没有使用。

1 个答案:

答案 0 :(得分:1)

让我们分析你的序列化字符串:

s:15:"collateralValue";N;s:15:"collateralGroup";R:15;

第一个属性(关键):

s:15:"collateralValue"
  • s只是意味着它是一个字符串
  • 15是字符串的大小
  • collateralValue是字符串本身的值(如果你看字符串是15个字符长)

第一个属性(值):

N
  • N只是意味着NULL

第二个属性(关键):

s:15:"collateralGroup"
  • s只是意味着它是一个字符串
  • 15是字符串的大小
  • collateralGroup是字符串本身的值(如果你看字符串是15个字符长)

第二个属性(值):

R:15
  • R表示参考
  • 15表示15值。所以这里的15值可能是属性collateralValue,这意味着如果更改它的值,它也会更改collateralGroup属性的值

有关详细信息,请参阅:http://www.phpinternalsbook.com/classes_objects/serialization.html