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

Serialized object coming out with internal value references

这是迄今为止我在 PHP 中看到的最奇怪的事情,但肯定有某种解释。

使用 serialize() 我正在存储一些对象。稍后,我使用 unserialize().

恢复它们

今天我发现了一个未序列化的对象的问题。想象一下这个场景:

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

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

当我这样做的时候:

$obj->collateralValue = 10;

并检查了对象变量,我被显示为:

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

令人难以置信!

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

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

显然这些 properties/variables 之间存在某种联系。我研究了 &NULL,发现的只有 this question,这告诉我我正在处理某种参考资料。

但是如何呢?

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

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

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

什么是 R:15?

会不会是问题所在?

如何解决这个问题,问题从何而来?


编辑

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

方位:

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

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
}

显然,我对每个 object__product_* 使用 __sleep() - return 那些变量名(包括 collateralValue 等等)。

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

非常混乱!


编辑#2

终于有了希望。

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

我仍然不知道为什么 PHP 在根本没有这些变量的情况下为唤醒的对象中的变量创建引用,但这些变量在 __sleep() 中被 return 编辑。

对我来说唯一明智的解决方案似乎是适应 __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() 将不会 return(这两个新的任何一个)变量名称(collat​​eralValue、collat​​eralGroup)在当前对象中未使用。

那么让我们分析一下你的序列化字符串:

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