The singleton pattern in PHP

  1. The singleton pattern::the good, the bad, and the ugly

    We use the singleton pattern in order to restrict the number of instances that can be created from a resource consuming class to only one.

    Resource consuming classes are classes that might slow down our website or cost money. For example:

    So, in all of these cases, it is a good idea to restrict the number of objects that we create from the expensive class to only one.

    • Some external service providers (APIs) charge money per each use.
    • Some classes that detect mobile devices might slow down our website.
    • Establishing a connection with a database is time consuming and slows down our app.

     

  2. The anatomy of a singleton pattern

    Let's start by understanding the structural characteristics of a class that obeys the singleton pattern:

    1. private constructor is used to prevent the direct creation of objects from the class.
    2. The expensive process is performed within the private constructor.
    3. The only way to create an instance from the class is by using a static method that creates the object only if it wasn't already created.

     

    // General singleton class.
    class Singleton {
      // Hold the class instance.
      private static $instance = null;
      
      // The constructor is private
      // to prevent initiation with outer code.
      private function __construct()
      {
        // The expensive process (e.g.,db connection) goes here.
      }
     
      // The object is created from within the class itself
      // only if the class has no instance.
      public static function getInstance()
      {
        if (self::$instance == null)
        {
          self::$instance = new Singleton();
        }
     
        return self::$instance;
      }
    }
  3. Why is it a singleton?

    Since we restrict the number of objects that can be created from a class to only one, we end up with all the variables pointing to the same, single object.

    // All the variables point to the same object.
    $object1 = Singleton::getInstance();
    $object2 = Singleton::getInstance();
    $object3 = Singleton::getInstance();

  4. Practical example::database class

    Let's demonstrate the singleton pattern with a class that establishes a database connection, and restricts the number of instances to only one.

    // Singleton to connect db.
    class ConnectDb {
      // Hold the class instance.
      private static $instance = null;
      private $conn;
      
      private $host = 'localhost';
      private $user = 'db user-name';
      private $pass = 'db password';
      private $name = 'db name';
       
      // The db connection is established in the private constructor.
      private function __construct()
      {
        $this->conn = new PDO("mysql:host={$this->host};
        dbname={$this->name}", $this->user,$this->pass,
        array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
      }
      
      public static function getInstance()
      {
        if(!self::$instance)
        {
          self::$instance = new ConnectDb();
        }
       
        return self::$instance;
      }
      
      public function getConnection()
      {
        return $this->conn;
      }
    }

    Since we use a class that checks if a connection already exists before it establishes a new one, it really doesn't matter how many times we create a new object out of the class, we still get the same connection. To prove the point, let's create three instances out of the class and var dump them.

    $instance = ConnectDb::getInstance();
    $conn = $instance->getConnection();
    var_dump($conn);
    
    $instance = ConnectDb::getInstance();
    $conn = $instance->getConnection();
    var_dump($conn);
    
    $instance = ConnectDb::getInstance();
    $conn = $instance->getConnection();
    var_dump($conn);

    The result is the same connection for the three instances.

 

0 Comments