29.2 Singleton
Singleton is a design pattern that is useful when you want to create an object that should be accessible for different, distinct parts of your application. Especially if this object is supposed to contain large chunks of information, instantiating it over and over again may prove to be extremely inefficient. Instead, if you had a way of sharing the same instance between all of the different parts of the application, it would be ideal. Of course, global variables come to mind, but they require you to manage initialization. That is, you must make sure that nobody erases this variable by mistake, that nobody instantiates another instance of this class, and so forth. Relying on the application code to properly use the infrastructure is definitely not object-oriented. In object-oriented design, you would instantiate your own class to expose an API allowing you to take care of these things in the class itself instead of having to rely on every piece of application code to maintain system integrity.
Figure 29.1 shows the structure of a Singleton implementation in PHP.
Analyzing this class, you can spot three key features: a private, static property holding the single instance; a public, static method that returns the single instance; and a private constructor.
A private, static property holds a single instantiation of the class. As previously mentioned in the description of static class properties, static variables are similar to global variables. In this case, however, we take advantage of our ability to make this property private, thereby preventing application code from reading it or changing it.
A public, static method returns the only instantiation of the class. This single access point allows us to initialize the variable exactly once, before the application code accesses it. Thanks to its being static, we don't need to instantiate an object before we can call this method.
Figure 29.1 Singleton pattern.
The constructor is private. A Singleton class is one of the few situations in which it makes sense to use a private constructor. The private constructor prevents users from instantiating the class directly. They must use the getInstance method. Trying to instantiate the class using $obj = new Singleton will result in a fatal error, since the global scope may not call the private constructor.
One real-world example with which you can use the Singleton class is a configuration class, which wraps around your application's configuration settings. Listing 29.1 is a simple example. Thanks to the Singleton pattern, there's never more than one copy of the configuration file in memory. Any changes made to the configuration automatically persist.
Listing 29.1 Configuration Singleton
<?php /* ** Configuration file singleton */ class Configuration { static private $instance = NULL; private $ini_settings; private $updated = FALSE; const INI_FILENAME = "/tmp/corephp.ini"; private function __construct() { if(file_exists(self::INI_FILENAME)) { $this->ini_settings = parse_ini_file(self::INI_FILENAME); } } private function __destruct() { //if configuration hasn't changed, no need //to update it on disk if(!$this->updated) { return; } //overwrite INI file with the //version in memory $fp = fopen(self::INI_FILENAME, "w"); if(!$fp) { return; } foreach ($this->ini_settings as $key => $value) { fputs($fp, "$key = \"$value\"\n"); } fclose($fp); } public function getInstance() { if(self::$instance == NULL) { self::$instance = new Configuration(); } return self::$instance; } public function get($name) { if(isset($this->ini_settings[$name])) { return $this->ini_settings[$name]; } else { return(NULL); } } public function set($name, $value) { //update only if different from what //we already have if(!isset($this->ini_settings[$name]) OR ($this->ini_settings[$name] != $value)) { $this->ini_settings[$name] = $value; $this->updated = TRUE; } } } //Test the class $config = Configuration::getInstance(); $config->set("username", "leon"); $config->set("password", "secret"); print($config->get("username")); ?>