/** * The Singleton class defines the `GetInstance` method that serves as an * alternative to constructor and lets clients access the same instance of this * class over and over. */ classSingleton { /** * The Singleton's instance is stored in a static field. This field is an * array, because we'll allow our Singleton to have subclasses. Each item in * this array will be an instance of a specific Singleton's subclass. You'll * see how this works in a moment. */ privatestatic $instances = [];
/** * The Singleton's constructor should always be private to prevent direct * construction calls with the `new` operator. */ protectedfunction__construct(){ }
/** * Singletons should not be cloneable. */ protectedfunction__clone(){ }
/** * Singletons should not be restorable from strings. */ publicfunction__wakeup() { thrownew \Exception("Cannot unserialize a singleton."); }
/** * This is the static method that controls the access to the singleton * instance. On the first run, it creates a singleton object and places it * into the static field. On subsequent runs, it returns the client existing * object stored in the static field. * * This implementation lets you subclass the Singleton class while keeping * just one instance of each subclass around. */ publicstaticfunctiongetInstance(): Singleton { $cls = static::class; if (!isset(self::$instances[$cls])) { self::$instances[$cls] = newstatic; }
returnself::$instances[$cls]; }
/** * Finally, any singleton should define some business logic, which can be * executed on its instance. */ publicfunctionsomeBusinessLogic() { // ... } }
/** * The client code. */ functionclientCode() { $s1 = Singleton::getInstance(); $s2 = Singleton::getInstance(); if ($s1 === $s2) { echo"Singleton works, both variables contain the same instance."; } else { echo"Singleton failed, variables contain different instances."; } }
clientCode();
Output.txt:
1
Singleton works, both variables contain the same instance.
/** * If you need to support several types of Singletons in your app, you can * define the basic features of the Singleton in a base class, while moving the * actual business logic (like logging) to subclasses. */ classSingleton { /** * The actual singleton's instance almost always resides inside a static * field. In this case, the static field is an array, where each subclass of * the Singleton stores its own instance. */ privatestatic $instances = [];
/** * Singleton's constructor should not be public. However, it can't be * private either if we want to allow subclassing. */ protectedfunction__construct(){ }
/** * Cloning and unserialization are not permitted for singletons. */ protectedfunction__clone(){ }
/** * The method you use to get the Singleton's instance. */ publicstaticfunctiongetInstance() { $subclass = static::class; if (!isset(self::$instances[$subclass])) { // Note that here we use the "static" keyword instead of the actual // class name. In this context, the "static" keyword means "the name // of the current class". That detail is important because when the // method is called on the subclass, we want an instance of that // subclass to be created here. self::$instances[$subclass] = newstatic; } returnself::$instances[$subclass]; } }
/** * The logging class is the most known and praised use of the Singleton pattern. * In most cases, you need a single logging object that writes to a single log * file (control over shared resource). You also need a convenient way to access * that instance from any context of your app (global access point). */ classLoggerextendsSingleton { /** * A file pointer resource of the log file. */ private $fileHandle;
/** * Since the Singleton's constructor is called only once, just a single file * resource is opened at all times. * * Note, for the sake of simplicity, we open the console stream instead of * the actual file here. */ protectedfunction__construct() { $this->fileHandle = fopen('php://stdout', 'w'); }
/** * Write a log entry to the opened file resource. */ publicfunctionwriteLog(string $message): void { $date = date('Y-m-d'); fwrite($this->fileHandle, "$date: $message\n"); }
/** * Just a handy shortcut to reduce the amount of code needed to log messages * from the client code. */ publicstaticfunctionlog(string $message): void { $logger = static::getInstance(); $logger->writeLog($message); } }
/** * Applying the Singleton pattern to the configuration storage is also a common * practice. Often you need to access application configurations from a lot of * different places of the program. Singleton gives you that comfort. */ classConfigextendsSingleton { private $hashmap = [];
/** * The client code. */ Logger::log("Started!");
// Compare values of Logger singleton. $l1 = Logger::getInstance(); $l2 = Logger::getInstance(); if ($l1 === $l2) { Logger::log("Logger has a single instance."); } else { Logger::log("Loggers are different."); }
// Check how Config singleton saves data... $config1 = Config::getInstance(); $login = "test_login"; $password = "test_password"; $config1->setValue("login", $login); $config1->setValue("password", $password); // ...and restores it. $config2 = Config::getInstance(); if ($login == $config2->getValue("login") && $password == $config2->getValue("password") ) { Logger::log("Config singleton also works fine."); }
Logger::log("Finished!");
Output:
1 2 3 4
2018-06-04: Started! 2018-06-04: Logger has a single instance. 2018-06-04: Config singleton also works fine. 2018-06-04: Finished!
Singletons have very little - if not to say no - use in PHP.
In languages where objects live in shared memory, Singletons can be used to keep memory usage low. Instead of creating two objects, you reference an existing instance from the globally shared application memory. In PHP there is no such application memory. A Singleton created in one Request lives for exactly that request. A Singleton created in another Request done at the same time is still a completely different instance. Thus, one of the two main purposes of a Singleton is not applicable here.
In addition, many of the objects that can conceptually exist only once in your application do not necessarily require a language mechanism to enforce this. If you need only one instance, then don't instantiate another. It's only when you may have no other instance, e.g. when kittens die when you create a second instance, that you might have a valid Use Case for a Singleton.
The other purpose would be to have a global access point to an instance within the same Request. While this might sound desirable, it really isnt, because it creates coupling to the global scope (like any globals and statics). This makes Unit-Testing harder and your application in general less maintainable. There is ways to mitigate this, but in general, if you need to have the same instance in many classes, use Dependency Injection.