Click here to Skip to main content
15,881,173 members
Articles / Web Development

PHP Singleton Pattern: A Step by Step And Problem-solving Implementation

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
11 Dec 2014CPOL4 min read 17.1K   8  
Design a Singleton Pattern in PHP from Scratch

Introduction

Design patterns are well-tested solutions to common problems in software development. In the famous the gang of four book, 23 design patterns were created and they have been widely used in Object-Oriented Programming, e.g. Java and C#. How about PHP? Since PHP 5.0, it has become a full Object-Oriented language, and some design patterns are also widely used in PHP and it numerous MVC frameworks. Today let’s implement the most-common Singleton Pattern in PHP, using a step by step and problem-solving approach.

Scenario

In PHP development (especially using MVC frameworks), we often need to include/require a DB class, a Upload class or a Cookie class, just as below:

PHP
<?php

require 'DB.class.php';

require 'Upload.class.php';

require 'Cookie.class.php';

but we need to make sure there is ONLY ONE instance of that DB or Cookie class, as one instance is enough and more will be problematic. This is when Singleton Pattern comes into the picture. Simply put, Singleton Pattern means that if a programmer has already instantiated an object of a certain class, another programmer cannot instantiate a second one. Now let’s see how to do it from scratch.

Implementation

Step One: Create a common class.

We just create a class named Singleton, and now we can instantiate any number of objects we want. Here we can create two objects using the class Singleton:

PHP
class Singleton {

   
}


$s1 = new Singleton();

$s2 = new Singleton();

Are $s1 and $s2 the same object? Of course not, and we can tell by using a simple if() statement:

PHP
class Singleton {

   
}


$s1 = new Singleton();

$s2 = new Singleton();


if ($s1 === $s2) {

    echo 's1 and s2 are the same object';

} else {

    echo 's1 and s2 are NOT the same object';

}

Note: when judging whether two objects are the same one, we must use “===”, not “==”.

 

Step Two: Forbid the “new” operation

If we want to prevent the class users from creating any number of Singleton class object that they want, how to do? Well, you might think that objects are created by the constructor of the class. So how about we conceal the constructor within the class itself so the outside could not use it, like below code does:

PHP
class Singleton2 {


    protected function __construct() {

    }


}


$s3 = new Singleton2(); 
//Fatal error: Call to protected Singleton2::__construct() from invalid context

Now you can see that we make the constructor protected, but the new problem is: no one can create an object anymore.

 

Step Three: Create an object-creation method

In order to let class users create an object, we need to leave an object-creation ‘interface’, so we add a new method called getIns(), and it is public(open to the outside) and static(so it can be called using class name):

PHP
class Singleton3 {


    public static function getIns() {

        return new self();

    }


    protected function __construct() {


    }


}

We just create a method within the classs. And within getIns(), we create an object of the class itself and then we return it. Now we test again:

PHP
class Singleton3 {


    public static function getIns() {

        return new self();

    }


    protected function __construct() {

    }


}


$s4 = Singleton3::getIns();

$s5 = Singleton3::getIns();


if ($s4 === $s5) {

    echo 's4 and s5 are the same object';

} else {

    echo 's4 and s5 are NOT the same object';

}

The result is:

s4 and s5 are NOT the same object

These two objects are still not the same object, why? Because Singleton3::getIns() is called twice and thus two objects are created. But now, the control is in our hands and we can just improve the getIns() to make it do what we want.

 

Step Four: Improve the getIns() method

Now we can add come checking in getIns():

PHP
class Singleton4 {


    protected static $ins = NULL;


    public static function getIns() {

        if (self::$ins === null) {

            self::$ins = new self();

        }


        return self::$ins;

    }


    protected function __construct() {


    }


}


$s6 = Singleton4::getIns();

$s7 = Singleton4::getIns();


if ($s6 === $s7) {

    echo 's6 and s7 are the same object';

} else {

    echo 's6 and s7 are NOT the same object';

}

You can see that now we store the instance of the class in a protected property called $ins. Then when getIns() is called, we have some checking going on here: if $ins is NULL, then we instantiate a class object. And at last, we return self::$ins.

Let’s test it:

s6 and s7 are the same object

Now we got the result we want! There is only one instance of the class now. But it is still now enough, why? If there is another class called Multi and it inherits our original class:

PHP
class Singleton4 {


    protected static $ins = NULL;


    public static function getIns() {

        if (self::$ins === null) {

            self::$ins = new self();

        }


        return self::$ins;

    }


    protected function __construct() {

        
    }


}


class Multi extends Singleton4 {

    public function __construct() {
      
    }

}


$s6 = Singleton4::getIns();

$s7 = Singleton4::getIns();


if ($s6 === $s7) {

    echo 's6 and s7 are the same object';

} else {

    echo 's6 and s7 are NOT the same object';

}


echo '<br>';

$s8 = new Multi();

$s9 = new Multi();

if ($s8 === $s9) {

    echo 's8 and s9 are the same object';

} else {

    echo 's8 and s9 are NOT the same object';

}

In this subclass, the visibility of the parent constructor is changed to public, and now what happens:

s6 and s7 are the same object
s8 and s9 are NOT the same object

This single line of code in the subclass:

PHP
public function __construct() {

}

destroyed everything we have done so far! So we need to prevent our subclass from changing visibility of parent class constructor.

 

Step Five: Prevent subclass overriding parent class constructor

Now we need to add the keyword final in front of our parent constructor:

PHP
class Singleton5 {


    protected static $ins = NULL;


    public static function getIns() {

        if (self::$ins === null) {

            self::$ins = new self();

        }


        return self::$ins;

    }


    // Adding final, so this method cannot be override!

    final protected function __construct() {


    }


}

So now if we run the script again:

PHP
class Singleton5 {


    protected static $ins = NULL;


    public static function getIns() {

        if (self::$ins === null) {

            self::$ins = new self();

        }


        return self::$ins;

    }


    // Adding final, so this method cannot be override!

    final protected function __construct() {


    }


}

We see the output:

Fatal error: Cannot override final method Singleton5::__construct()

Are we done now? Still not yet…If someone code:

PHP
class Singleton5 {


    protected static $ins = NULL;


    public static function getIns() {

        if (self::$ins === null) {

            self::$ins = new self();

        }


        return self::$ins;

    }


    // Adding final, so this method cannot be override!

    final protected function __construct() {

        
    }


}


$s10 = Singleton5::getIns();

$s11 = clone $s10;  // s11 cloned s10, then more than one object is created!


if ($s10 === $s11) {

    echo 's10 and s11 are the same object';

} else {

    echo 's10 and s11 are NOT the same object';

}

Result:

s10 and s11 are NOT the same object

This is because the clone keyword is used here. So the problem occurs again. How to prevent clone?

 

Step Six: Forbid __clone() magic function

PHP
class Singleton6 {


    protected static $ins = NULL;


    public static function getIns() {

        if (self::$ins === null) {

            self::$ins = new self();

        }


        return self::$ins;

    }


    // Adding final, so this method cannot be override!

    final protected function __construct() {


    }

   

    // Forbid clone

    final protected function __clone(){

 
    }


}


$s10 = Singleton6::getIns();

$s11 = clone $s10;  // Fatal error: Call to protected Singleton6::__clone()

Now clone is not possible.

Finally, we have completed our Singleton Pattern in PHP!

I hope this step by step approach to attain singleton could be helpful for you. Knowing a design pattern is good, but I guess if we know why we need to create such a pattern and how to iteratively improve our code to achieve our goal is more important and useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Australia Australia
Chris Yu is a full stack web developer, a Zend Certified PHP Engineer. His current interest focuses on front-end web component, as well as front-end & back-end separation development.

Comments and Discussions

 
-- There are no messages in this forum --