PHP4 Tricks: The Singleton Pattern – Part I

This is the first of a series of “PHP4 Tricks” articles detailing programming techniques that I have used with PHP4. These tricks are centered on object-oriented programming. They were necessary during the pre-PHP5 days when OOP was a bit of a pain to implement OOP constructs and GoF patterns in PHP4. These days, PHP5 is gaining rapid widespread adoption. But there are still systems and shared webhosts out there that do not support PHP5 at the time of writing. There are also a lot of systems out there that are written in PHP4 that do not translate well into PHP5.

PHP5 supports a whole slew of object-oriented keywords and features. This makes it easier to properly implement the design patterns featured in the GoF book. That said, new systems that are based on GoF patterns and practices should be programmed using PHP5 instead of PHP4. PHP4 does not have all the necessary OOP keywords and features required to fully implement these patterns. But for the rest of us who are stuck with PHP4, we have no choice but to resort to tricks in order to implement most of the commonly used patterns.

We begin with one of the most commonly used patterns (at least in my experience), the Singleton design pattern.

5 May 2007: Fixed assignment operator based on “References with global and static variables” section and as pointed out in one of the comments.

One Instance To Rule Them All

The Singleton design pattern in programming is used when only one instance of a class is required throughout the lifetime of the program. Good OOP design suggests that one should avoid using global variables as much as possible. There are good reasons for not using globals, especially in PHP where a single misstep can create a gaping security hole in the entire program. Depending on the server setup, it may be possible to override the value of a program’s global variable if register_globals is set by passing a GET parameter to the script’s URL.

There are a lot of good discussions out there on the Internet as to why globals should be avoided. It’s not in the scope of this article to cover them so this is an exercise left to the reader.

PHP4 Singleton How-To

So what does a Singleton look like in PHP4? Here is a very simple implementation:

    class Singleton {
        function Singleton() {
            // Perform object initialization here.
        }

        function &getInstance() {
            static $instance = null;
            if (null === $instance) {
                $instance =& new Singleton(); // This will not work as expected. See http://php.net/static
                $instance = new Singleton();
            }
            return $instance;
        }
    }

Normally, a class implemented using the Singleton Pattern will have its constructor set to private to prevent the client code from creating more than one instance of the class. Since PHP4 does not have the private keyword, we cannot do that. Instead, we are forced to trust the client code to not create an instance of our Singleton class directly. While some may see this as a problem, in practice this is rarely a problem as we can see with Python and Perl5 OO implementations. IMHO it’s often better to leave it to the programmer to decide on the accessibility of an object’s property or method. The OO purist will argue otherwise regarding this matter. But since PHP4 does not implement nor enforce access modifiers so there is no point on debating this.

So how do we instantiate a class that we are not supposed to instantiate directly? Simple. We provide an interface method to allow the client code to retrieve the current instance of the class. We do this from the getInstance() which does a “lazy instantiation” of our singleton object and returns a reference to it. When the getInstance() method is first called, our local static variable, $instance is initialized to null. Since the instance does not yet exist, it will be created for us and the reference to it is returned to our client code. Subsequent calls to getInstance() will reuse or previously created instance, ensuring that only one instance of our Singleton class exists throughout the lifetime of our program.

Caveats

The above implementation has one problem to it though. To create another Singleton class, one would have to rewrite getInstance() tailored for the new class. We can define a class that provides a uniform instantiation procedure and a change getInstance() so that it’s only an interface method. Then we just have our subclass call on that procedure from the interface method.

For example:

    class SingletonInterface {
        function SingletonInterface() {
            // Perform object initialization here.
        }

        function &__getInstanceImp($name) {
            static $instances = array();
            if (!isset($instances[$name])) {
                $instances[$name] =& new $name(); // This will not work as expected. See http://php.net/static
                $instances[$name] = new $name();
            }
            return $instances[$name];
        }

        function &getInstance() {
            trigger_error('SingletonInterface::getInstance() needs to be overridden in a subclass.', E_USER_ERROR);
        }
    }

    class MySingleton extends SingletonInterface {
        function MySingleton() {
            // Perform object initialization here.
            parent::SingletonInterface();
        }

        function &getInstance() {
            return parent::__getInstanceImp('MySingleton');
        }
    }

We implement a new “private” method here called __getInstanceImp which takes the name of a class as a parameter. This method takes care of creating our singleton instance. Note that we store our singleton instance in an array $instances. This is because __getInstanceImp() will run within the context of SingletonInterface. If we did not use an array here, we could possibly overwrite a previously instantiated singleton from another class.

This is a bit of an improvement for two reasons:

  1. We guarantee to our client code that all Singletons in our program can be instantiated using a single interface method getInstance().

  2. We no longer have to keep rewriting the code needed to create the singleton instance.

By subclassing our Singleton class from SingletonInterface() however, we have introduced another problem: we now need to call on the parent constructor, which in PHP4 can be a bit redundant and error prone. In PHP4 a method becomes a constructor when it has the same name as the class. PHP5 provides a better way of implementing constructors through __construct(). It is possible to simulate this in PHP4 using a trick found in CakePHP:

    class Object {
        function Object() {
            $args = func_get_args();
            if (method_exists($this, '__destruct')) {
                register_shutdown_function(array(&$this, '__destruct'));
            }
            call_user_func_array(array(&$this, '__construct'), $args);
        }

        function __construct() {
        }
    }

As a bonus, we also get __destruct() behavior for our objects. We now rewrite our SingletonInterface using our new Object superclass:

    class SingletonInterface extends Object {
        function __construct() {
            // Perform object initialization here.
        }

        function &__getInstanceImp($name) {
            static $instances = array();
            if (!isset($instances[$name])) {
                $instances[$name] = new $name(); // No changes necessary here.
            }
            return $instances[$name];
        }

        function &getInstance() {
            trigger_error('SingletonInterface::getInstance() needs to be overridden in a subclass.', E_USER_ERROR);
        }
    }

    class MySingleton extends SingletonInterface {
        function __construct() {
            // Perform object initialization here.
            parent::__construct();
        }

        function &getInstance() {
            return parent::__getInstanceImp('MySingleton');
        }
    }

Notice that we no longer have to provide a method with the same name as our class to get constructors to work. All we needed was a __construct() method. To know why this works, I suggest reading up on constructors from any PHP4 reference book or the [PHP4 Constructors][php4constructors] section in the online manual.

END OF PART ONE

In the next part of this article, we will be taking a look at how this implementation of a Singleton in PHP4 can be used.

[php4constructors]: http://www.php.net/manual/en/language.oop.constructor.php “PHP: Constructors – Manual”

2 Responses to “PHP4 Tricks: The Singleton Pattern – Part I”

  1. Janci Says:

    Actually, there seems to be a problem with storing references in static variables [Check http://www.php.net/static paragraph "References with global and static variables"].

    Therefore in the getInstance() method we cannot use:

    $instance = & new Singleton();

    We have to use the version without reference:

    $instance = new Singleton();

    That is ok in most of the cases and whan it becomes problem you can always use a global variable instead of static, but you have to use the superglobal array $GLOBALS to access the global variable, not the global modifier:

    function &getInstance() {
        if (!isset($GLOBALS['instance']) || !is_object($GLOBALS['instance']) || !is_a($GLOBALS['instance'], 'Singleton')) {
            $GLOBALS['instance'] =& new Singleton();
        }
        return $instance;
    }
  2. nimrod.abing Says:

    Thanks for pointing out that section of the manual. It’s the little things like these that frustrate me to no end when working with PHP4. PHP5 is no different, it has an entirely different set of caveats that you have to keep in mind as well.

Leave a Reply

Comments are moderated by the administrator. If this is your first time posting a comment, your comment will go to a moderation queue and it may take a while for your comment to appear. Or it may get deleted.