Singleton Pattern

The Singleton Pattern is a creational design pattern that ensures a class has only one instance and provides a global access point to it. This article explains how Singleton works, when to use it, when to avoid it, and how to implement it correctly in PHP.

Jun 10, 2026
Singleton Pattern

Singleton Pattern

The Singleton Pattern is one of the most well-known creational design patterns in Object-Oriented Programming. It is used when a class should have only one instance during the lifetime of an application, and that instance should be accessible from a controlled global access point.

Singleton is simple to understand, but it is also one of the most debated design patterns. It can be useful in specific situations, but it can also create hidden dependencies, testing problems, and tightly coupled code when overused. This article explains how the Singleton Pattern works, when it can be useful, when it should be avoided, and how to implement it in PHP.

Introduction

In the previous article of this Design Patterns series, we introduced the general idea of design patterns and explained how they help developers solve common software design problems. We also discussed the main categories of design patterns: creational, structural, and behavioral patterns.

The Singleton Pattern belongs to the creational design patterns category. Creational patterns focus on how objects are created and how object creation can be controlled or simplified.

Singleton is commonly used to control access to shared resources such as configuration objects, loggers, cache managers, database connection managers, and application-level services. However, modern software design often prefers dependency injection over direct Singleton usage, especially in large and testable applications.

What Is the Singleton Pattern?

The Singleton Pattern is a design pattern that restricts a class to only one object instance and provides a way to access that same instance from different parts of the application.

In simple terms, Singleton answers this question: how can we make sure that only one object of a class exists?

For example, an application may need one configuration manager that stores application settings. Instead of creating many configuration manager objects, the Singleton Pattern ensures that all parts of the application use the same instance.

Main Idea of Singleton

The main idea of Singleton is based on three rules:

  • The class stores its own single instance internally.

  • The constructor is private or protected to prevent direct object creation from outside.

  • The class provides a public static method that returns the single instance.

Because the constructor is not publicly accessible, external code cannot create new objects using the new keyword. Instead, it must call a method such as getInstance.

Why Singleton Is Used

Singleton is used when an application needs exactly one shared instance of a class. This can help avoid repeated object creation and can ensure that a shared state or shared resource is accessed consistently.

Common reasons for using Singleton include:

  • Controlling access to a shared resource.

  • Ensuring one instance of a configuration manager.

  • Providing one logger instance.

  • Managing a shared cache object.

  • Avoiding repeated initialization of expensive objects.

However, developers should be careful. Just because a class can be Singleton does not mean it should be Singleton. The pattern should solve a real design problem.

Basic Singleton Structure

A basic Singleton class usually contains a private static property, a private constructor, and a public static method for accessing the instance.

class Singleton
{
    private static ?Singleton $instance = null;

    private function __construct()
    {
    }

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

        return self::$instance;
    }
}

In this example, the static property stores the single instance. The getInstance method checks if the instance already exists. If it does not exist, the method creates it. If it already exists, the method returns the same object.

This process is sometimes called lazy initialization because the object is created only when it is needed for the first time.

Singleton Example in PHP

The following example shows a more practical Singleton implementation for an application configuration manager:

class ConfigManager
{
    private static ?ConfigManager $instance = null;

    private array $settings = [];

    private function __construct()
    {
        $this->settings = [
            'app_name' => 'My Application',
            'environment' => 'production',
            'debug' => false,
        ];
    }

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

        return self::$instance;
    }

    public function get(string $key): mixed
    {
        return $this->settings[$key] ?? null;
    }
}

$config = ConfigManager::getInstance();
echo $config->get('app_name');

In this example, the ConfigManager class can only be created once. Any part of the application that calls ConfigManager::getInstance will receive the same instance.

Preventing Cloning in Singleton

In PHP, an object can be cloned using the clone keyword. To protect the Singleton from being duplicated, developers usually prevent cloning by making the __clone method private.

class ConfigManager
{
    private static ?ConfigManager $instance = null;

    private function __construct()
    {
    }

    private function __clone()
    {
    }

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

        return self::$instance;
    }
}

By making __clone private, external code cannot clone the Singleton object.

Preventing Unserialization in Singleton

Another way an object could be recreated is through serialization and unserialization. In modern PHP versions, developers can prevent unserialization by defining a __wakeup method that throws an exception.

class ConfigManager
{
    private static ?ConfigManager $instance = null;

    private function __construct()
    {
    }

    private function __clone()
    {
    }

    public function __wakeup(): void
    {
        throw new Exception('Cannot unserialize a singleton.');
    }

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

        return self::$instance;
    }
}

This helps protect the Singleton from being recreated through unserialization.

Lazy Initialization

Lazy initialization means that the Singleton instance is created only when it is requested for the first time. This can be useful when creating the object is expensive or when the object may not always be needed during a request.

In the basic Singleton implementation, lazy initialization happens inside the getInstance method. The object is not created until getInstance is called.

This can reduce unnecessary resource usage, but it also means the initialization logic is hidden inside the class. In larger applications, dependency injection may provide clearer control over when and how objects are created.

Eager Initialization

Eager initialization means that the Singleton instance is created before it is requested. This approach is less common in PHP web applications because PHP requests are usually short-lived, but the concept is useful in other environments.

With eager initialization, the object exists immediately. This can make access simpler, but it may create unused objects and increase startup cost.

Lazy initialization is usually more common in PHP Singleton examples.

Real Use Cases of Singleton

The Singleton Pattern can be useful in some real software situations, especially when exactly one instance is needed and the object does not need to be replaced often.

Possible use cases include:

  • Configuration manager: A shared object that reads and provides application settings.

  • Logger: A central logging object used across the application.

  • Cache manager: A shared cache access object.

  • Application registry: A central place for shared application-level values.

  • Resource manager: A class that controls access to a limited resource.

Even in these cases, developers should evaluate whether dependency injection or a service container would provide a cleaner design.

Singleton for Logger Example

A simple logger can be implemented as a Singleton when the application needs one shared logging instance.

class Logger
{
    private static ?Logger $instance = null;

    private function __construct()
    {
    }

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

        return self::$instance;
    }

    public function log(string $message): void
    {
        echo date('Y-m-d H:i:s') . ' - ' . $message;
    }
}

$logger = Logger::getInstance();
$logger->log('Application started.');

This example is simple, but in real applications, a logger may write to files, databases, cloud services, or external monitoring tools. In such cases, using an injected logger interface is often more flexible than hardcoding a Singleton logger.

Benefits of Singleton Pattern

The Singleton Pattern has several benefits when used correctly. It gives developers control over object creation and ensures that only one instance exists.

Main benefits include:

  • Ensures a single shared instance of a class.

  • Provides a global access point to that instance.

  • Can reduce repeated object creation.

  • Can centralize access to shared resources.

  • Can be simple to implement in small applications.

These benefits make Singleton attractive to beginners, but the pattern also has important drawbacks.

Drawbacks of Singleton Pattern

The Singleton Pattern can create problems if it is used too often or in the wrong place. One of the biggest issues is that it introduces global state into the application.

Global state can make code harder to understand because any part of the application may access and modify the same object. This can lead to hidden dependencies and unexpected behavior.

Singleton can also make testing harder. If a class directly calls ConfigManager::getInstance or Logger::getInstance, it becomes difficult to replace that dependency with a fake object during unit tests.

Another problem is tight coupling. Code that depends directly on a Singleton class becomes strongly connected to that specific implementation.

Singleton and Testing Problems

Testing is one of the main reasons many developers avoid Singleton in modern applications. Unit tests work best when dependencies can be replaced easily.

If a class uses dependency injection, a test can provide a fake dependency. But if a class directly calls a Singleton, replacing that dependency becomes more difficult.

For example, a service that calls Logger::getInstance directly is tied to the Logger class. A better design may be to depend on a LoggerInterface and inject the logger from outside.

Singleton vs Dependency Injection

Dependency injection is often a better alternative to Singleton in modern software design. Instead of asking a class to fetch its dependency from a global access point, the dependency is passed into the class from outside.

For example, instead of this:

class OrderService
{
    public function completeOrder(): void
    {
        $logger = Logger::getInstance();
        $logger->log('Order completed.');
    }
}

A dependency injection approach would look like this:

class OrderService
{
    public function __construct(
        private LoggerInterface $logger
    ) {
    }

    public function completeOrder(): void
    {
        $this->logger->log('Order completed.');
    }
}

The second approach is more flexible and easier to test. The service does not know whether the logger is a file logger, database logger, cloud logger, or fake test logger.

Singleton vs Static Class

Singleton and static classes may look similar because both can be accessed without manually creating objects in the calling code. However, they are not the same.

A Singleton is still an object. It can implement interfaces, use inheritance, and hold object state. A static class is accessed directly through static methods and usually does not require an object instance.

Static classes are often used for simple helper methods. Singleton is used when one object instance should exist. Both approaches should be used carefully because they can increase coupling if overused.

Singleton in Modern Frameworks

Modern frameworks such as Laravel and Symfony often use service containers to manage object lifetimes. A service container can register a class as a shared service, which means the same instance is reused when requested.

This provides Singleton-like behavior without forcing the class itself to implement the Singleton Pattern.

This approach is usually better because the class remains clean and testable. The decision to share one instance is handled by the framework configuration or service container instead of being hardcoded inside the class.

Singleton in Laravel

In Laravel, developers can bind a class as a singleton inside a service provider using the service container:

$this->app->singleton(LoggerInterface::class, FileLogger::class);

This tells Laravel to create only one shared instance for that binding. Other classes can depend on LoggerInterface, and Laravel will inject the same FileLogger instance.

This is different from writing a manual Singleton class. The class itself does not need a private constructor or getInstance method. The container controls the lifetime of the object.

When to Use Singleton

Use the Singleton Pattern only when there is a strong reason to guarantee that exactly one instance of a class exists and when global access does not harm the design.

Singleton may be acceptable when:

  • The object represents a truly shared application-level resource.

  • Only one instance should logically exist.

  • The object does not need to be replaced often in tests.

  • The application is small and does not require complex dependency management.

  • A framework service container is not available.

Even then, developers should think carefully before using it.

When to Avoid Singleton

Avoid Singleton when the class has dependencies that may change, when the code must be easy to test, or when multiple implementations may be needed in the future.

Singleton should usually be avoided when:

  • The class depends on external services.

  • The class must be replaced with a mock or fake during testing.

  • The application uses dependency injection or a service container.

  • The Singleton creates hidden global state.

  • Multiple configurations or multiple instances may be needed later.

  • The pattern is used only for convenience.

Using Singleton only for easy access is usually a sign of weak design.

Best Practices for Singleton Pattern

If Singleton is used, it should be implemented carefully and only in limited places.

Useful best practices include:

  • Use Singleton only when one instance is logically required.

  • Keep Singleton classes simple and focused.

  • Prevent direct construction using a private constructor.

  • Prevent cloning if duplicate instances are not allowed.

  • Prevent unserialization when needed.

  • Avoid storing too much mutable global state.

  • Prefer dependency injection in large applications.

  • Use framework service containers when available.

These practices help reduce the risks of Singleton and keep the application design cleaner.

Common Mistakes with Singleton

One common mistake is using Singleton for every service because it is easy to access. This creates a hidden global structure and makes the application harder to test.

Another mistake is using Singleton to avoid learning dependency injection. While Singleton may look simpler at first, it can create long-term maintenance problems.

A third mistake is storing request-specific data inside a Singleton. Shared instances should not accidentally keep data that belongs only to one user request or one operation.

A fourth mistake is treating Singleton as a replacement for good architecture. Singleton controls object creation, but it does not automatically make the design clean.

Singleton and Thread Safety

In some programming languages and runtime environments, Singleton implementation must consider thread safety. If multiple threads try to create the instance at the same time, more than one object could be created unless the code is protected.

In typical PHP web applications, each request usually runs separately, so thread safety is less of a concern than in languages such as Java or C#. However, developers working with long-running PHP processes, workers, or asynchronous environments should still understand object lifetime and shared state carefully.

Singleton in Small Projects vs Large Projects

In small scripts or simple applications, Singleton may be acceptable for configuration or simple shared utilities. It can be easy to implement and easy to understand.

In large projects, Singleton is often less suitable because it can hide dependencies and make testing harder. Large applications usually benefit more from dependency injection, interfaces, service containers, and clear object lifetimes.

The size and complexity of the project should influence the decision. A pattern that is acceptable in a small script may create problems in a large enterprise application.

Practical Checklist Before Using Singleton

Before using the Singleton Pattern, developers should ask these questions:

  • Does this class truly need only one instance?

  • Is global access safe for this object?

  • Will this make testing harder?

  • Could dependency injection solve the problem better?

  • Will this object store mutable shared state?

  • Could the application need multiple instances later?

  • Is this pattern being used for design reasons or only for convenience?

If the answers show that Singleton adds more risk than value, another design approach should be used.

Conclusion

The Singleton Pattern is a creational design pattern that ensures a class has only one instance and provides a global access point to that instance. It can be useful for shared resources such as configuration managers, loggers, and cache managers in specific situations.

However, Singleton should be used carefully. It can create hidden dependencies, global state, tight coupling, and testing difficulties when applied without a clear reason. In many modern applications, dependency injection and service containers provide a cleaner and more flexible alternative.

For developers learning design patterns, Singleton is important to understand because it introduces key ideas about object creation and shared instances. But professional software design requires knowing not only how to implement Singleton, but also when to avoid it.