Skip to Content
Course content

134: The Singleton Pattern in PHP

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Look, you've probably seen the word "Singleton" tossed around in design pattern discussions, and it often comes with a warning that it's an "anti-pattern." Before we get into the controversy, let's focus on what it actually does: it ensures a class has only one instance and provides a global point of access to it. In PHP, where every request starts from a blank slate, this is mostly about managing resources within a single execution cycle.

Why would I actually need this instead of just creating a new object?

Imagine you're building a system that connects to a database. Establishing a connection is "expensive"—it takes time and memory. If you're passing a database object around to twenty different classes in one request, you don't want each of those classes calling new DatabaseConnection(). If you do, you'll end up with twenty open connections to your MySQL server for a single page load. That's a great way to crash your server under moderate load.

The Singleton pattern solves this by saying: "If an instance already exists, just give me that one. If not, create it once and save it for later."

How do I stop someone from just using the new keyword?

This is the "secret sauce" of the Singleton. To prevent other developers (or your future self) from accidentally instantiating the class, you make the constructor private. If you try to call new DatabaseConnection() from outside the class, PHP will throw a fatal error.

We also need a static property to hold the instance. Because it's static, it belongs to the class itself, not to any specific object instance.

class DatabaseConnection {
    // This holds our single instance
    private static $instance = null;

    // Private constructor prevents external instantiation
    private function __construct() {
        // Imagine heavy connection logic here
        echo "Connecting to the database... ";
    }

    // We also need to stop people from cloning the object
    private function __clone() {}

    // And stop them from unserializing it
    public function __wakeup() {
        throw new \Exception("Cannot unserialize a singleton.");
    }
}

How do I actually get the instance if I can't use new?

Since the constructor is locked away, we create a public static method—usually called getInstance(). This method acts as the gatekeeper. It checks if the static property is still null; if it is, it creates the object. Otherwise, it just hands back the one it already has.

class DatabaseConnection {
    private static $instance = null;

    private function __construct() {
        // Connection logic...
    }

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function query($sql) {
        return "Executing: $sql";
    }
    
    // ... (clone and wakeup methods from above)
}

// Usage:
$db1 = DatabaseConnection::getInstance();
$db2 = DatabaseConnection::getInstance();

// $db1 and $db2 are the exact same instance. 
// The constructor was only called once.

Is it true that Singletons are "bad" or an "anti-pattern"?

I'll be honest with you: in modern PHP development, we tend to avoid writing our own Singletons. Why? Because they introduce "global state," which makes unit testing a nightmare. If a class depends on a Singleton, you can't easily swap that Singleton out for a "mock" object during a test.

Most professional projects now use a Dependency Injection Container (like the ones in Laravel or Symfony). The container manages the object's lifecycle for you—essentially acting as a Singleton manager without baking that rigid logic directly into your business classes. However, you'll still see Singletons in legacy code and small scripts, and understanding how they work is non-negotiable for any senior dev.




📋 Practical Task

Build a Global Application Configuration Manager

Your task is to create a class called AppConfig that implements the Singleton pattern. This class should be used to load system settings (like an API key or environment name) from an array, ensuring the file/array is only "loaded" into memory once per request.

  • Make the constructor private.
  • Create a static property $instance.
  • Implement a public static method getInstance().
  • Add a private property $settings which is initialized in the constructor with an associative array (e.g., ['api_key' => '12345', 'env' => 'production']).
  • Add a public method get($key) that returns the value from the $settings array based on the provided key.
  • Verify that calling AppConfig::getInstance() multiple times returns the same object and doesn't re-initialize the settings array.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.