SQRCAT
SQRCAT

Reputation: 5840

Serialized object coming out with internal value references

This is by far the strangest thing i have seen in PHP, but there surely is some sort of explanation.

Using serialize() i am storing some objects. At a later point, i revive them using unserialize().

Today i discovered a problem with an object that has been unserialized. Picture this scenario:

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

Now imagine $obj to be an instance of object__product_bundle as shown above.

When i did:

$obj->collateralValue = 10;

And checked the object variables, i was shown:

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

Mindboggling!

I spent an hour smashing my head against the table, as this didn't make sense. But when i started using var_dump() on the object, before making changes to it, i saw this:

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

Apparently these properties/variables were somehow linked. I researched &NULL and all i found was this question which told me i am dealing with some sort of references.

But how?

My object comes from a serialized string.

Now, taking a look at the serialized string i found this:

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

What is R:15 ?

Can it be the issue?

How can this problem be addressed and where does it come from?


EDIT

After digging deeper, i found the culprit.

Orientiation:

The objects (as described above) are stored into a property of another object, which is the item of a shop cart.

class shopCart {
    public $storage;
}

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

class shopCart_item {
    public $object;
}

$object is where the products (object__product_*) are stored.

Upon placing an order, with the aim of being repeated (subscription), this entire shopCart is stored into the database as a blob.

Whenever a subscription order is scheduled, an automated task then grabs the old shopCart and generates a new order from it.

And here i found the culprit - i added the properties (collateralValue etc.) later during development, but there had already been stored orders.

Now during debugging i found that this is where PHP starts creating references, although i do not understand why.

Simply put:

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, i use __sleep() for each object__product_* - which returns those variable names (including collateralValue and so on).

The question now becomes then: Why does PHP create references, when it is dealing with new properties for objects that were asleep but whose structure has changed in the meantime?

Very confusing!


EDIT #2

Finally some hope.

My __sleep() function basically returned a hardcoded array of variable names, as there were a ton of others i never wanted to store in the database. This approach apparently led to the current problem described in this question.

I still do not know why PHP creates references for variables in objects that were awoken without having those variables at all, but with those variables being returned in __sleep().

The only sensible solution to me, seemed to be to adapt __sleep(). I now do this:

public function __sleep(){

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

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

}

This way, __sleep() will not return (any of those two new) variable names (collateralValue, collateralGroup) which are not in use in the current object.

Upvotes: 2

Views: 67

Answers (1)

Rizier123
Rizier123

Reputation: 59701

Well let's analyse your serialized string:

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

First property (key):

s:15:"collateralValue"
  • s just means it is a string
  • 15 is the size of the string
  • collateralValue is the string itself the value (And if you look the string is 15 characters long)

First property (value):

N
  • N just mean NULL

Second property (key):

s:15:"collateralGroup"
  • s just means it is a string
  • 15 is the size of the string
  • collateralGroup is the string itself the value (And if you look the string is 15 characters long)

Second property (value):

R:15
  • R means reference
  • 15 means to the 15 value. So here the 15 value is probably the property collateralValue, which means if you change the value of it it also changes the value of the collateralGroup property

For more information see: http://www.phpinternalsbook.com/classes_objects/serialization.html

Upvotes: 1

Related Questions