views:

347

answers:

2

I am writing a fairly complex PHP applications where a single user action can trigger changes in many other sub-systems, and I'm contemplating using an observer pattern. However, I am wondering if I have to re-create all the objects involved.

Is it possible to while serializing objects to store their relationships? For example

$equipmentHandler = new EquipmentHandler();
$character = new Character();
$character->subscribeOnEquipmentChanged($equipmentHandler);

$_SESSION['character'] = serialize($character);
$_SESSION['subscriber'] = serialize($equipmentHandler);

Will the relationship be preserved after unserializing? Or do I have do lump them all into one object?

$cache['character'] = $character;
$cache['subscriber'] = $equipmentHandler;
$_SESSION['cache'] = serialize($cache);

Any advice would be appreciated.

(PS. The character data requires many DB requests to create and I am thinking of storing it by doing a write to cache and DB, but only read from cache policy, so it will be serialized anyway)

A: 

According to the manual of the serialize function:

The value to be serialized. serialize() handles all types, except the resource-type. You can even serialize() arrays that contain references to itself. Circular references inside the array/object you are serializing will also be stored. Any other reference will be lost.

When serializing objects, PHP will attempt to call the member function __sleep prior to serialization. This is to allow the object to do any last minute clean-up, etc. prior to being serialized. Likewise, when the object is restored using unserialize() the __wakeup member function is called.

So I guess it is not possible unless you do something smart in the _sleep and _wakeup

Henri
+1  A: 

A relation will be kept, but it will be different than you expect. When you serialize two instances of Character that both refer to the same EquipmentHandler, you're going to get two separate instances of this EquipmentHandler, instead of the single one you expected. As this example illustrates:

<?php

echo "BEFORE SERIALIZE:\n";
class A { }
class B { }

$a = new A;
$b = new B;
$a -> b = $b;

$a2 = new A;
$a2 -> b = $b;

var_dump($a->b);
var_dump($a2->b);

echo "AFTER SERIALIZE:\n";
$a3 = unserialize(serialize($a));
$a4 = unserialize(serialize($a2));

var_dump($a3->b);
var_dump($a4->b);

The output of this is:

BEFORE SERIALIZE:
object(B)#2 (0) {
}
object(B)#2 (0) {
}
AFTER SERIALIZE:
object(B)#5 (0) {
}
object(B)#7 (0) {
}

Look for the number after the pound. This refers to the object ID within PHP. Before serializing both $a->b and $a2->b refer to an object with object ID #2: the same instance. But after the serialization they refer to object IDs #5 and #7: different instances.

This may, or may not, be a problem for you.

To restore the connection to one single B object, you're going to have to get a little tricky. You could use the __sleep() handler in A to flatten the actual reference to an INSTANCE of B to just a mentioning of B: "I had a reference to B". Then implement the __wakeup() handler using that mentioning of a B instance in A to acquire a single instance of a new B object.

BTW. The PHP session extension already does serializing automatically, no need for you to pre-serialize it yourself :)

MathieuK
Would explicitly specifying that you are passing a reference through the addSubscriber() function removes the duplication of instances?
Extrakun
Nope, afraid not. The serialization format is not capable of defining an object in that way.
MathieuK