 |
Account Login
|
 |
 |
Latest Articles
|
 |
 |
IRC Channel
|
 |
 |
Associates
|
 |
 |
Associates
|
 |
|
 |
|
 |
|
 |
10-14-2007, 04:28 PM
|
#1 (permalink)
|
|
The Reckoner
Join Date: Sep 2007
Posts: 437
Thanks: 22
|
How to use the Singleton design pattern
The Singleton Pattern is one of the GoF (Gang of Four) Patterns. This particular pattern provides a method for limiting the number of instances of an object to just one. It's an easy pattern to grasp once you get past the strange syntax used.
Consider the following class:
PHP Code:
class Database
{
public function __construct() { ... }
public function connect() { ... }
public function query() { ... }
...
}
This class creates a connection to our database. Any time we need a connection we create an instance of the class, such as:
PHP Code:
$pDatabase = new Database();
$aResult = $pDatabase->query('...');
Lets say we use the above method many times during a script's lifetime, each time we create an instance we're creating a new Database object (we're also creating a new database connection, but that's irrelevant in this example) and thus using more memory. Sometimes you may intentionally want to have multiple instances of a class but in this case we don't.
The Singleton method is a solution to this common problem. To make the Database class a Singleton we first need to add a new property to the class, we'll call this $m_pInstance:
PHP Code:
class Database
{
// Store the single instance of Database
private static $m_pInstance;
...
}
As the comment states, this property will be used to store the single instance of our Database class. You should also note that this property must be static.
Next we need to change the constructor's scope to private. This is one of the strange syntaxes that usually confuse people.
PHP Code:
class Database
{
// Store the single instance of Database
private static $m_pInstance;
private function __construct() { ... }
}
By making the constructor private we have prohibited objects of the class from being instantiated from outside the class. So for example the following no longer works outside the class:
PHP Code:
$pDatabase = new Database();
We now need to add a method for creating and returning our Singleton. Add the following method to the Database class:
PHP Code:
public static function getInstance()
{
if (!self::$m_pInstance)
{
self::$m_pInstance = new Database();
}
return self::$m_pInstance;
}
This funny looking function is responsible for handling our object instance. It's relatively easy to understand, basically we check our static property $m_pInstance, if it is not valid we create a new instance of the Database class by calling the constructor. Remember, we made the __construct() method private, so an instance of the object can only be created from within the class' methods. Finally the function returns a reference to our static property. On subsequent calls to getInstance(), $m_pInstance will be valid and thus the reference will be returned - no new instances are created.
So our Database class now looks something like this
PHP Code:
class Database
{
// Store the single instance of Database
private static $m_pInstance;
private function __construct() { ... }
public static function getInstance()
{
if (!self::$m_pInstance)
{
self::$m_pInstance = new Database();
}
return self::$m_pInstance;
}
}
You can now get an instance of the Database class from anywhere (without using globals or function arguments) in your project. Here's an example and comparison:
This is the usual way we create objects:
PHP Code:
$pDatabase = new Database();
$aResult = $pDatabase->query('...');
This is the Singleton way:
PHP Code:
$pDatabase = Database::getInstance();
$aResult = $pDatabase->query('...');
To conclude, the Singleton is an easy-to-use design pattern for limiting the number of instances of an object. See the attached files for working examples.
__________________
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
|
|
|
|
|
The Following 4 Users Say Thank You to Karl For This Useful Post:
|
|
10-14-2007, 06:24 PM
|
#2 (permalink)
|
|
The Prestige
Join Date: Sep 2007
Location: Sweden, Stockholm
Posts: 1,080
Thanks: 115
|
Looks nice :D I will study the examples ;)
And thanks for a great tutorial :)
|
|
|
|
|
The Following User Says Thank You to Tanax For This Useful Post:
|
|
10-20-2007, 06:16 PM
|
#3 (permalink)
|
|
The Prestige
Join Date: Oct 2007
Location: Manchester, UK
Posts: 854
Thanks: 32
|
to further enforce this pattern you may want to either make the magic method __clone() private or just make the script die when this function is called, this is so that clones of the singleton object cannot be made.
__________________
mysql> SELECT * FROM `users` WHERE `users`.`clue` > 0;
Empty set (0.00 sec)
|
|
|
|
11-01-2007, 12:45 AM
|
#4 (permalink)
|
|
La Vida es Sueño
Join Date: Sep 2007
Location: Oldham
Posts: 2,280
Thanks: 90
|
What a great PHP singleton tutorial. By implementing the __clone method you certainly won't be getting past it!
__________________
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
|
|
|
11-01-2007, 09:39 AM
|
#5 (permalink)
|
|
The Contributor
Join Date: Sep 2007
Posts: 31
Thanks: 0
|
Great tutorial, I will definitely use this technique in my coding.
|
|
|
|
05-08-2008, 08:52 PM
|
#6 (permalink)
|
|
The Visitor
Join Date: May 2008
Posts: 1
Thanks: 0
|
I'm having some trouble getting my Singleton to hold state.
I have a class that implements the Singleton pattern and has a member called $sessionState which is meant to hold a string value denoting the current state of the Singleton instance.
Now, maybe it's my lack of understanding when it comes to sessions, but every time I retrieve an instance of my Singleton class, the $sessionState member gets reset.
Code fragments follow:
PHP Code:
class Control{
private static $sessionState;
private static $instance;
// private constructor
private function __construct(){
// initalize state value
self::$sessionState='Not Initialized';
}
// the getInstance() method returns a single instance of the object
public static function getInstance(){
if(empty(self::$instance)){
self::$instance=new Control();
}
return self::$instance;
}
public function Initialize(){
$return; // either 'true' or 'false'
switch(self::$sessionState){
case 'Not Initialized':
$monitor=SessionMonitor::getInstance();
$initialized=$monitor->initialize();
if($initialized=='false'){
$this->setErrorCode('102');
$return='false';
}else{
self::$sessionState='Running';
$this->setErrorCode('0');
$return='true';
}
break;
case 'Running':
$this->setErrorCode('103');
$return='false';
break;
case 'Terminated':
$this->setErrorCode('104');
$return='false';
break;
default:
$this->setErrorCode('101');
$return='false';
}
return $return;
}
}
As you can see, I want the Initialize method to check the current value of $sessionState and respond accordingly. After a successful intialization of SessionMonitor, the $sessionState should be updated from 'Not Initialized' to 'Running' (which testing proves DOES happen). I'd expect, then, that any subsequent calls to other Control methods which depend on the value of $sessionState would read a value of 'Running'.
Such as...
PHP Code:
public function GetValue($cmi,$dbConn){
$return; //either a string value or empty string
switch(self::$sessionState){
case 'Not Initialized':
$this->setErrorCode('122');
$return='';
break;
case 'Running':
// get the type of model element
$cmiObject=$this->getCMIObject($cmi);
//
$return=$cmiObject->getValue($dbConn);
if($return==false){
$error=$cmiObject->getError();
$this->setErrorCode($error->errorCode);
$return='';
}else{
$this->setErrorCode('0');
}
break;
case 'Terminated':
$this->setErrorCode('123');
$return='';
break;
default:
$this->setErrorCode('101');
$return='';
}
return $return;
}
If I call this method (after a successful call to Initialize), the case block for 'Running' doesn't execute, as the value for $sessionState seems to get reset to 'Not Initialized'.
What am I missing? Are Singleton instances cleared/collected with subsequent http requests to the same php file?
|
|
|
|
08-25-2009, 07:01 PM
|
#7 (permalink)
|
|
The Visitor
Join Date: Aug 2009
Posts: 2
Thanks: 0
|
Quote:
Originally Posted by Karl
PHP Code:
class Database { public function __construct() { ... } public function connect() { ... } public function query() { ... } ... }
|
Something I'll usually do is overload the class name with a function:
PHP Code:
function Database() { return Database::getInstance(); }
This, in combination with usually returning an object simplifies instruction chaining (and, as a result, code readability) greatly.
PHP Code:
public function rowCount() { return Database() ->query('select %s from `%q` LIMIT 1', 'count(*) as "count"', $this->table ) ->fetchObject() ->count; }
|
|
|
|
08-25-2009, 07:06 PM
|
#8 (permalink)
|
|
The Visitor
Join Date: Aug 2009
Posts: 2
Thanks: 0
|
Quote:
Originally Posted by Prpl_Ppl_Etr
I'm having some trouble getting my Singleton to hold state.
I have a class that implements the Singleton pattern and has a member called $sessionState which is meant to hold a string value denoting the current state of the Singleton instance.
|
Oooo... session-level singleton. Not a bad idea, especially in the case of, for example, a User or Visitor model.
Still, your best bet with that is to store the class instance in the $_SESSION variable, and define the __sleep and __wakeup methods accordingly.
Quote:
|
What am I missing? Are Singleton instances cleared/collected with subsequent http requests to the same php file?
|
Yes. Yes they are. In fact, between requests, the only things that hang out are the contents of $_SESSION (if session_start() has been called), files, and databases. No other variables are retained.
|
|
|
|
08-27-2009, 04:38 PM
|
#9 (permalink)
|
|
The Wanderer
Join Date: Aug 2009
Location: Torquay
Posts: 16
Thanks: 2
|
A very useful design pattern indeed, thanks for the clear tutorial Karl
|
|
|
09-25-2009, 10:01 AM
|
#10 (permalink)
|
|
That guy
Join Date: Sep 2009
Location: San Antonio, TX
Posts: 24
Thanks: 0
|
Great
Great tutorial. It actually looks a lot like the way I did my singleton pattern.. lol
Here it is:
PHP Code:
class Config
{
const host = "localhost";
const user = "root";
const pass = "pass";
const name = "name";
}
final class Singleton extends Config
{
protected static $connection;
protected static $database = null;
protected function Singleton () { }
protected function __clone () { }
static function Prepare()
{
self::$connection = new mysqli
(
parent::host,
parent::user,
parent::pass,
parent::name
);
if (mysqli_connect_errno())
{
printf
(
"Connection Error: %s\n ", mysqli_connect_error()
);
} else { echo "Databse resource found."; }
}
public static function Instance()
{
if (!isset(
self::$database)) {
self::$database = self::Prepare();
}
return self::$database;
}
}
$instance = Singleton::Instance();
|
|
|
|
10-05-2009, 02:46 PM
|
#11 (permalink)
|
|
The Addict
Join Date: Nov 2007
Location: the Netherlands
Posts: 281
Thanks: 2
|
Why would you call your DB class "Singleton"? Singleton is the name of the pattern, not a standard name for a DB class..
__________________
Nunchaku! Who doesn't like martial arts? =)
|
|
|
10-06-2009, 12:06 PM
|
#12 (permalink)
|
|
The Wanderer
Join Date: Oct 2009
Posts: 5
Thanks: 0
|
Singleton and PHP?
Yeah, and why all this talk about singleton in PHP when ALL variables containing a singleton is destroyed on new request.
It seems we have this great pattern that is absolutely useless when it comes to singleton and sessions.
How do I store a language variable in a session using a singleton without it being destroyed on every request.
I'd like to avoid having to add a lang=n to all links througout a whole website, but store a language value i session using a singleton?
|
|
|
|
10-07-2009, 12:41 AM
|
#13 (permalink)
|
|
The Addict
Join Date: May 2009
Posts: 287
Thanks: 5
|
Quote:
Originally Posted by svdelle
Yeah, and why all this talk about singleton in PHP when ALL variables containing a singleton is destroyed on new request.
It seems we have this great pattern that is absolutely useless when it comes to singleton and sessions.
How do I store a language variable in a session using a singleton without it being destroyed on every request.
I'd like to avoid having to add a lang=n to all links througout a whole website, but store a language value i session using a singleton?
|
You could store it in a cookie or database.
|
|
|
|
10-07-2009, 10:09 AM
|
#14 (permalink)
|
|
The Wanderer
Join Date: Oct 2009
Posts: 5
Thanks: 0
|
And that would meen a roundtrip to a file on the client, or the database on the server. It seems pretty lame, that you cannot simply store a single instance of an object in a server variable.
|
|
|
|
10-07-2009, 10:04 PM
|
#15 (permalink)
|
|
That guy
Join Date: Sep 2009
Location: San Antonio, TX
Posts: 24
Thanks: 0
|
Quote:
Originally Posted by Jim
Why would you call your DB class "Singleton"? Singleton is the name of the pattern, not a standard name for a DB class..
|
It was an example, brosef.
|
|
|
|
12-30-2009, 02:46 PM
|
#16 (permalink)
|
|
The Visitor
Join Date: Dec 2009
Posts: 1
Thanks: 0
|
Quote:
Originally Posted by cachepl0x
Great tutorial. It actually looks a lot like the way I did my singleton pattern.. lol
Here it is:
PHP Code:
class Config
{
const host = "localhost";
const user = "root";
const pass = "pass";
const name = "name";
}
final class Singleton extends Config
{
protected static $connection;
protected static $database = null;
protected function Singleton () { }
protected function __clone () { }
static function Prepare()
{
self::$connection = new mysqli
(
parent::host,
parent::user,
parent::pass,
parent::name
);
if (mysqli_connect_errno())
{
printf
(
"Connection Error: %s\n ", mysqli_connect_error()
);
} else { echo "Databse resource found."; }
}
public static function Instance()
{
if (!isset(
self::$database)) {
self::$database = self::Prepare();
}
return self::$database;
}
}
$instance = Singleton::Instance();
|
great tut.... how about this? removing the extra method prepare() and moving the code into the __cunstruct
class Config
{
const DB_HOST = "localhost";
const DB_USERNAME = "uuu";
const DB_PASSWORD = "pppp";
const DB_NAME = "bbbb";
}
final class Database extends Config
{
protected static $dbConnection;
protected static $database = NULL;
protected function __construct () {
self::$dbConnection = new mysqli
(
parent::DB_HOST,
parent::DB_USERNAME,
parent::DB_PASSWORD,
parent::DB_NAME
);
if (mysqli_connect_errno())
{
throw new Exception('Unable to connect to database');
}
}
protected function __clone () { }
public static function getInstance()
{
if (!isset(
self::$database)) {
self::$database = new Database();
}
return self::$database;
}
}
|
|
|
|
12-31-2009, 07:24 AM
|
#17 (permalink)
|
|
The Contributor
Join Date: Feb 2007
Posts: 64
Thanks: 9
|
I have been using the Singleton for awhile now and ran into unexpected results with an app recently.
I most often used the singleton to get an instance of a class throughout several parts of a framework I wrote. No other method other than the singleton instance was static.
When I used it, it would return the wrong results. For example, an authorization controller I wrote, upon checking if a used was logged in ( a simple bool ), would always return false. Then I tried this...
Code:
if (Authorization::instance()->logged_in() === false)
which fixed the problem. If it helps, I also had a factory method for chaining and what not. I know the singleton was setup right to return the instance, I just don't know what was causing the issue and still haven't figured it out. Maybe authorization was just a bad idea for a singleton, but either way its working quite well now.
|
|
|
|
12-31-2009, 06:52 PM
|
#18 (permalink)
|
|
is cute and cuddly
Join Date: Mar 2008
Location: Vegas, Baby
Posts: 963
Thanks: 31
|
It's hard to tell without seeing the entire class, but you must have had an error somewhere in the implementation of the singleton - it should simply return an instance of your object from a static property where the instance is being held. Outside of generating the instance, it should have no effect on the object or its methods whatsoever. If the object was not responding properly, there was possibly either an error in the object, or in the way it was generated.
|
|
|
|
01-11-2010, 02:59 PM
|
#19 (permalink)
|
|
The Wanderer
Join Date: Jan 2010
Posts: 7
Thanks: 0
|
One thing that I usually do and, which in case of this example won't change anything is instead of instantiating the class with its classname I instantiate it with "self":
Instead of doing this:
PHP Code:
self::$m_pInstance = new Database();
I do this:
PHP Code:
self::$m_pInstance = new self();
Then you don't depend on the classname and if you ever change its name you don't have to also go through the code.
|
|
|
|
06-22-2010, 06:51 AM
|
#20 (permalink)
|
|
The Visitor
Join Date: Jun 2010
Location: Hermosillo
Posts: 2
Thanks: 0
|
I have a another example of signletone in php for a connection to a database using myqli for anyone who likes use this.
Code:
class DataBaseConnection {
static private $instance = NULL;
private $objMySqli;
private function __construct() {
$this->objMySqli=new mysqli("host","user","passwd","database_name");
}
static public function getInstance() {
if (self::$instance == NULL) self::$instance = new self;
return self::$instance;
}
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
public function getMySqli(){
return $this->objMySqli;
}
}
//Usage
$connection= DataBaseConnection::getInstance();//getting the instance forom of the class DataBaseConnection
$result = $connection->getMySqli()->query("select * from tablename");
while($row=$result->fetch_row()){
//print the data of the $row variable
}
$connection2 = new DataBaseConnection()//this will produce an error
|
|
|
|
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
|
|
|
| Thread Tools |
Search this Thread |
|
|
|
| Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
|