Serialization context

Introduction

Sometimes, especially when we are working with serializable classes, it is desirable that an instance of a closure to be wrapped only by a single Opis\Closure\SerializableClosure class, in order to avoid multiple serialization of the same closure instance. Let’s take the following example:

use Opis\Closure\SerializableClosure;

$function = function(){
    return "Hello World";
};

$collection = array(
    'a' => new SerializableClosure($function),
    'b' => new SerializableClosure($function),
);

//Serialize
$collection = serialize($collection);
//Unserialize
$collection = unserialize($collection);

$a = $collection['a']->getClosure();
$b = $collection['b']->getClosure();

//Prints FALSE
print $a === $b ? 'TRUE' : 'FALSE';

As we can see in the above example, even though the same closure instance was serialized, after the deserialization two different instances of the same closure were created.

This happened because the same closure was wrapped by two different Opis\Closure\SerializableClosure objects and therefore, when those objects were deserialized and the closures extracted, resulted two distinct closure instances.

Avoiding this kind of situations is done by serializing closures in context:

use Opis\Closure\SerializableClosure;

$function = function(){
    return "Hello World";
};

SerializableClosure::enterContext();

$collection = array(
    'a' => SerializableClosure::from($function),
    'b' => SerializableClosure::from($function),
);

SerializableClosure::exitContext();

//Serialize
$collection = serialize($collection);
//Unserialize
$collection = unserialize($collection);

$a = $collection['a']->getClosure();
$b = $collection['b']->getClosure();

//Prints TRUE
print $a === $b ? 'TRUE' : 'FALSE';

The enterContext method

If a serialization context wasn’t allready created by a previous call to this method, then a new serialization context is created. Each serialization context has an associated list of serialized closures and an internal counter which is incremented each time this method is called.

Each call to this method must have a matching call to Opis\Closure\SerializableClosure::exitContext method.

The from method

Wraps a given closure inside a Opis\Closure\SerializableClosure object, keeping a record of all closures that were wrapped in the current serialization context. When calling this method, if a closure was already wrapped, the coresponding Opis\Closure\SerializableClosure object will be returned.

This method can be used even if a serialization context wasn’t created at call time.

The exitContext method

Decrements the internal counter of the current serialization context, if any. When the counter reaches to zero, the current serialization context is destroyed.