Designs patterns exists for a long time ago, before informations systems. They have been use in building
Designs patterns are class models to approach somes problems that you can meet in yours developpements
They are numerous, I'll try to explains the majors
Somes oriented object programming basics are required to fully understand this article, in particular :
- Classes, instances
- Extends a class
- Interface
- Static variables
- Public, protected and private attributes
The design patterns studied here are:
Design pattern summary
Even if you are not familiar with design patterns, perharps you have heard about Singleton.
This design pattern allow to have only one instance of a class.
Examples usage are numerous: configuration class, database object, etc...In this article, we'll use a configuration class.
TopUsage
The class must lock the constructor: Constructor is private
So, to build the object, we have a static method which build the object and send the object reference if this object has yet been build
After, you can use your instance like another objectNote about the PHP 5 object model: By default, a clone method exists __clone which allow to copy any object. In our case, this method will be a problem. So we'll override this method and define it throwing an exception
So we define an interface for a singleton object
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
if (!interface_exists('Singleton_Interface')) {
/**
* Define interface singleton
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @package Poo_Example
* @subpackage Singleton
*/
interface Singleton_Interface
{
/**
* Retrieve the singleton instance
* @access public
* @static
*/
public static function getInstance();
/**
* Override clone method to throw exception
* @access public
*/
public function __clone();
}
}
Without this design pattern
This basic example show you how can we build a configuration class without singleton design pattern.It read configuration values from an ini file.
Class used is the following:
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
if (!class_exists('Singleton_Configuration_Without')) {
/**
* Simple configuration class exemple which load configuration values
* from an ini file in the class path
* @package Poo_Example
* @subpackage Singleton
*/
class Singleton_Configuration_Without
{
/**
* @var string Name of the configuration file
* @access private
*/
private $_configurationFile = 'singleton_config_ini.php';
/**
* @var array Configurations values loaded
* @access private
*/
private $_configurationsValues = array();
/**
* Constructor
* @access public
* @return void
*/
public function __construct()
{
echo "in the class constructor\n";
$file = dirname(__FILE__).'/'.$this->_configurationFile;
$this->_configurationsValues = parse_ini_file($file);
}
/**
* Retrieve a configuration value
* @access public
* @param string $configurationKey : The configuration key
* @return string
*/
public function getValue($configurationKey)
{
return $this->_configurationsValues[$configurationKey];
}
}
}
And the usage script is the following:
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
/**
*
*/
define('__EXAMPLE_PATH__', realpath(dirname(__FILE__).'/../../'));
require_once __EXAMPLE_PATH__ . '/Autoload.php';
/**
* $configurationContent = "database_user = root\n".
* "database_password =\n".
* "database_host = localhost\n".
* "database_database = database\n";
* file_put_contents(__EXAMPLE_PATH__.'/config/singleton_config.ini', $configurationContent);
* @package Poo_Example
* @subpackage Singleton
*/
$config = new Singleton_Configuration_Without();
echo "configuration value for the key database_host : [".
$config->getValue('database_host')."]\n";
$anotherConfig = new Singleton_Configuration_Without();
echo "configuration value for the key for key database_user : [".
$config->getValue('database_user')."]\n";
echo "config is ConfigurationClass instance? [".
($config instanceof Singleton_Configuration_Without)."]\n";
echo "anotherConfig is ConfigurationClass instance? [".
($anotherConfig instanceof Singleton_Configuration_Without)."]\n";
echo "sames references? ".($config === $anotherConfig)."\n";
?>
In command line usage, we have the following output:
configuration value for the key database_host : [localhost]
in the class constructor
configuration value for the key for key database_user : [root]
config is ConfigurationClass instance? [1]
anotherConfig is ConfigurationClass instance? [1]
sames references?
So you can see that we go each time in the constructor and instances are distincts
TopWith design pattern
Singleton usage on it class has been made by:
- Updating constructor visibility to private
- Static getInstance method definition
- Overwiting the __clone method
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
if (!class_exists('Singleton_Configuration_With')) {
/**
*
*/
if (!defined('__EXAMPLE_PATH__')) {
define('__EXAMPLE_PATH__', realpath(dirname(__FILE__).'/../'));
}
require_once __EXAMPLE_PATH__ . '/Autoload.php';
/**
* Simple configuration class based on the singleton design pattern
* which load configuration values from an ini file in the class path
* @package Poo_Example
* @subpackage Singleton
*/
class Singleton_Configuration_With implements Singleton_Interface
{
/**
* @var string Name of the configuration file
* @access private
*/
private $_configurationFile = 'singleton_config_ini.php';
/**
* @var array Configurations values loaded
* @access private
*/
private $_configurationsValues = array();
/**
* @var mixed The singleton instance
* @access private
* @static
*/
private static $_instance = null;
/**
* Retrieve the singleton instance
* @access public
* @static
* @return mixed
*/
public static function getInstance()
{
if (is_null(self :: $_instance)) {
self :: $_instance = new Singleton_Configuration_With();
}
return self :: $_instance;
}
/**
* Constructor
* @access private
* @return void
*/
private function __construct()
{
echo "in the class constructor\n";
$file = dirname(__FILE__).'/'.$this->_configurationFile;
$this->_configurationsValues = parse_ini_file($file);
}
/**
* Retrieve a configuration value
* @access public
* @param string $configurationKey : The configuration key
* @return string
*/
public function getValue($configurationKey)
{
return $this->_configurationsValues[$configurationKey];
}
/**
* Override clone method to throw exception
* @access public
* @return void
* @throws Exception
*/
public function __clone()
{
throw new Exception('You cannot clone singleton object');
}
}
}
Execution script has been update to take care of the getInstance method :
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
/**
*
*/
define('__EXAMPLE_PATH__', realpath(dirname(__FILE__).'/../../'));
require_once __EXAMPLE_PATH__ . '/Autoload.php';
/**
* $configurationContent = "database_user = root\n".
* "database_password =\n".
* "database_host = localhost\n".
* "database_database = database\n";
* file_put_contents(__EXAMPLE_PATH__.'/config/singleton_config.ini', $configurationContent);
* @package Poo_Example
* @subpackage Singleton
*/
$config = Singleton_Configuration_With :: getInstance();
echo "configuration value for the key database_host : [".
$config->getValue('database_host')."]\n";
$anotherConfig = Singleton_Configuration_With :: getInstance();
echo "configuration value for the key for key database_user : [".
$config->getValue('database_user')."]\n";
echo "config is ConfigurationSingletonClass instance? [".
($config instanceof Singleton_Configuration_With )."]\n";
echo "anotherConfig is ConfigurationSingletonClass instance? [".
($anotherConfig instanceof Singleton_Configuration_With )."]\n";
echo "sames references? ".($config === $anotherConfig)."\n";
?>
The output is the following:
configuration value for the key database_host : [localhost]
configuration value for the key for key database_user : [root]
config is ConfigurationSingletonClass instance? [1]
anotherConfig is ConfigurationSingletonClass instance? [1]
sames references? 1
As you can see, we go in the constructor only one time. And each time, we have the same object
TopConclusion
With this pattern, we ensure that you'll have only one class instance.
In others languages, multi threads, you must ensure in constructor that another process isn't yet in constructor with a lock system. With PHP, this problem doesn't exists
To be sure that you doesn't forgot to make a method (example overrite clone method), it is better to use an interface to define your singleton.
Top
: 




