Press "Enter" to skip to content

Serialize/Unserialize Breaks the Singleton

We often define a Singleton this way:

class Singleton {
    private static $instance = NULL;
    /** do not allow direct constructor calls */
    private function __construct() {
    }
    /** do not allow deep copy */
    private function __clone() {
    }
    public static function getInstance() {
        if (NULL === self::$instance) {
        	self::$instance = new self();
		}
        return self::$instance;
    }
}

Many people remember to protect against deep copy, but actually we overlook one point:

<?php
$a = Singleton::getInstance();
$b = unserialize(serialize($a));
var_dump($a === $b);
//bool(false)

Haha, so we still need to patch it up — add protection against serialization:

class Singleton {
    private static $instance = NULL;
    /** do not allow direct constructor calls */
    private function __construct() {
    }
    /** do not allow deep copy */
    private function __clone() {
    }
    /** do not allow serialize */
    private function __sleep() {
    }
    /** do not allow unserialize */
    private  function __wakeup() {
    }
    public static function getInstance() {
        if (NULL === self::$instance) {
        	self::$instance = new self();
		}
        return self::$instance;
    }
}

However, sometimes we want our Singleton class to be serializable. In that case, consider the following approach:

class Singleton {
    private static $instance = NULL;
    /** do not allow direct constructor calls */
    private function __construct() {
    }
    /** do not allow deep copy */
    private function __clone() {
    }
    public  function __wakeup() {
        self::$instance = $this;
    }
    /** cleanup is needed when the singleton switches over */
    public function __destruct() {
        self::$instance = NULL;
    }
    public static function getInstance() {
        if (NULL === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

Note in the above: on wakeup we switch the current singleton instance, in order to guarantee the singleton at the moment of serialization/deserialization.
Also, for some Singleton classes that hold global resources, we need to define a destructor to reclaim resources during the switch.
Now, please look carefully, then think about whether this code has any problems.
Keep reading: under some conditions, this code may not achieve our goal, for example:

$a = Singleton::getInstance();
$a = unserialize(serialize($a));
var_dump($a === Singleton::getInstance());
//bool(false)

Everyone can think about why. If you don't want to think, check out my next article.
Finally, an ad,,, follow me on Sina Weibo: http://t.sina.com.cn/laruence, 🙂

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.