1. Home
  2. Coding
  3. Extending singleton in PHP to avoid boilerplate code

Extending singleton in PHP to avoid boilerplate code

Share

If we make extensive use of the Singleton pattern in PHP and our project has become large, we can extend a single class that handles all the Singleton instances in order to make the code more readable and less verbose.

We can take advantage of PHP classes and have an Object-Oriented approach, but extending a Singleton class in PHP can be confusing. If we want to succeed, we need to:

  1. understand the problem of extending a Singleton class in PHP
  2. the right way to it

Also, you can find in this Github repository the example code of this article.

What is a Singleton

First of all, we need to make a brief introduction. What is a singleton? In object-oriented programming, the singleton is one of the fundamental patterns that is meant to ensure that only one instance of a given class is created.

Singleton classes are usually used for caching, handling connections to databases, logging, or storing preferences. Let’s see below a simple example of a Singleton class in PHP:

<?php

/**
 * Simple singleton that we will extend
 */
class Singleton {

  /**
   * @var Singleton $instance Instance
   */
  private static $instance;

  /**
   * Construct
   */
  private function __construct() {
  }

  /**
   * Get Instance
   *
   * @return Singleton Instance
   */
  public static function get_instance() {
    if ( ! isset( self::$instance ) ) {
      self::$instance = new self();
    }

    return self::$instance;
  }
}

Therefore, to create a Singleton class in PHP we must use a get_instance method to always return a single instance and a private constructor to always prevent the creation of a new object.

The problem extending a Singleton class in PHP

What happens if we need to create multiple Singleton classes? Are we forced to repeat the same boilerplate code in all our classes?

Let’s take again this code as an example:

<?php

/**
 * Simple singleton that we will extend
 */
class Singleton {

  /**
   * @var Singleton $instance Instance
   */
  private static $instance;

  /**
   * Construct
   */
  private function __construct() {
  }

  /**
   * Get Instance
   *
   * @return Singleton Instance
   */
  public static function get_instance() {
    if ( ! isset( self::$instance ) ) {
      self::$instance = new self();
    }

    return self::$instance;
  }
}

As we said, we can take advantage of PHP classes and extend the Singleton class we have created. So, let’s try this way:

<?php

/**
 * Extended Singleton
 */
class ExtendedSingleton extends Singleton {
  /**
   * ExtendedSingleton print_hello_new
   */
  public function print_hello() {
    echo 'hello';
  }
}

Unfortunately, it doesn’t work. If we test the code we wrote:

<?php

$extended_singleton = ExtendedSingleton::get_instance();
$extended_singleton->print_hello();

we see an error that looks like this:

Fatal error: Uncaught Error: Call to undefined method Singleton::print_hello_new() in /var/www/html/index.php:10 Stack trace: #0 {main} thrown in /var/www/html/index.php on line 10

In other words, this is happening because get_instance() is returning an instance of the Singleton class instead of ExtendedSingleton.

How to extend a Singleton in PHP correctly

To handle multiple “single instances of a class” using a single class, we can extend the Singleton using this simple solution:

<?php

/**
 * Original Singleton class
 */
abstract class Singleton {

  /**
   * Any Singleton class.
   *
   * @var Singleton[] $instances
   */
  private static $instances = array();

  /**
   * Consctruct.
   * Private to avoid "new".
   */
  private function __construct() {
  }

  /**
   * Get Instance
   *
   * @return Singleton
   */
  final public static function get_instance() {
    $class = get_called_class();

    if ( ! isset( $instances[ $class ] ) ) {
      self::$instances[ $class ] = new $class();
    }

    return self::$instances[ $class ];
  }

  /**
   * Avoid clone instance
   */
  private function __clone() {
  }

  /**
   * Avoid serialize instance
   */
  private function __sleep() {
  }

  /**
   * Avoid unserialize instance
   */
  private function __wakeup() {
  }
}

In the $instances variable we are creating a single instance for each class that extends Singleton.php. Also, we make private __construct, __clone, __sleep and __wakeup to avoid new classes, cloning, serialization and unserialization of the instance.

To test that this is working, we do:

<?php

$extended_singleton = ExtendedSingleton::get_instance();
$extended_singleton->print_hello();
echo '<br />' . get_class( $extended_singleton );

that outputs:

hello

ExtendedSingleton

Nice and easy. You can find in this Github repository the example code used in this article.

Sources. This solution is inspired by multiple sources:

If you like our post, please share it: