abstractionz
asked on
Singleton in PHP not really a Singleton?
Let's say that I have the following singleton class:
==========
<?php
class Singleton
{
private static $instance;
private function __construct(){}
public static function getInstance()
{
if(!isset(self::$instance) )
{
$object= __CLASS__;
self::$instance=new $object;
}
return self::$instance;
}
public function displayMessage()
{
echo 'I am a Singleton';
}
}
?>
==========
Now I have two scripts, test.php, and test2.php
They each look like the following:
test.php
==========
<?php
require_once "singleton.php";
$singleton=Singleton::getI nstance();
$singleton->displayMessage ();// display message
$singleton2=Singleton::get Instance() ;
$singleton2->displayMessag e();// display message
print "$singleton<br />";
print "$singleton2<br />";
?>
==========
test2.php
==========
require_once "singleton.php";
$e = new Exception();
$singleton=Singleton::getI nstance();
$singleton->displayMessage ();// display message
$singleton2=Singleton::get Instance() ;
$singleton2->displayMessag e();// display message
print "$singleton<br />";
print "$singleton2<br />";
==========
Note that test2.php has created an exception object at the top. So the output of test and test2 is the following:
I am a Singleton
I am a Singleton
Object id #1
Object id #1
I am a Singleton
I am a Singleton
Object id #2
Object id #2
So the Singleton only acts as a 'Singleton' within the current script. On requests to other pages, it does NOT return the same object id. On test, the object id is #1, but on test2, the object id is 2. So this is not really a singleton like in Java? I don't see how it could be since there is no container. I thought maybe PHP did something behind the scenes. And if you put some intialization code in the private constructor, it will run on every getInstance on separate scripts. So if you have code that connects to the database, it is no different then connecting at the top of every script. Am I missing something??
==========
<?php
class Singleton
{
private static $instance;
private function __construct(){}
public static function getInstance()
{
if(!isset(self::$instance)
{
$object= __CLASS__;
self::$instance=new $object;
}
return self::$instance;
}
public function displayMessage()
{
echo 'I am a Singleton';
}
}
?>
==========
Now I have two scripts, test.php, and test2.php
They each look like the following:
test.php
==========
<?php
require_once "singleton.php";
$singleton=Singleton::getI
$singleton->displayMessage
$singleton2=Singleton::get
$singleton2->displayMessag
print "$singleton<br />";
print "$singleton2<br />";
?>
==========
test2.php
==========
require_once "singleton.php";
$e = new Exception();
$singleton=Singleton::getI
$singleton->displayMessage
$singleton2=Singleton::get
$singleton2->displayMessag
print "$singleton<br />";
print "$singleton2<br />";
==========
Note that test2.php has created an exception object at the top. So the output of test and test2 is the following:
I am a Singleton
I am a Singleton
Object id #1
Object id #1
I am a Singleton
I am a Singleton
Object id #2
Object id #2
So the Singleton only acts as a 'Singleton' within the current script. On requests to other pages, it does NOT return the same object id. On test, the object id is #1, but on test2, the object id is 2. So this is not really a singleton like in Java? I don't see how it could be since there is no container. I thought maybe PHP did something behind the scenes. And if you put some intialization code in the private constructor, it will run on every getInstance on separate scripts. So if you have code that connects to the database, it is no different then connecting at the top of every script. Am I missing something??
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
How do you put the instance into the Session?
I tried placing a database connection object into the session in one script, then in another tried to retrieve it and got the following fatal error:
Fatal error: main() [function.main]: The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition "DB_Mysql" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide a __autoload() function to load the class definition in C:\phpapps\\test2.php on line 10
I tried placing a database connection object into the session in one script, then in another tried to retrieve it and got the following fatal error:
Fatal error: main() [function.main]: The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition "DB_Mysql" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide a __autoload() function to load the class definition in C:\phpapps\\test2.php on line 10
To hold an object in the session you have to instance the object before
getting or setting it to the session.
// To set the object...
require_once("MyObject.php ");
session_start();
$_obj = new MyObject("param1", "param2", "param3");
$_SESSION['obj'] = $_obj;
// To get the object...
require_once("MyObject.php ");
session_start();
$_obj = new MyObject();
$_obj = $_SESSION['obj'];
This way, PHP knows what the object is before it loads it.
Make sense? I know it probably seems weird, but that's the
way it works in PHP.
Anyway, give it a shot.
--brian
getting or setting it to the session.
// To set the object...
require_once("MyObject.php
session_start();
$_obj = new MyObject("param1", "param2", "param3");
$_SESSION['obj'] = $_obj;
// To get the object...
require_once("MyObject.php
session_start();
$_obj = new MyObject();
$_obj = $_SESSION['obj'];
This way, PHP knows what the object is before it loads it.
Make sense? I know it probably seems weird, but that's the
way it works in PHP.
Anyway, give it a shot.
--brian
ASKER
But it would not work if using a Singleton DB Connection object.
Right, being that the whole point of the Singleton pattern is a single instantiation.
Typically, though, the Singleton class can handle the second instantiation attempt
and basically just ignore it.
Anyway, this is a pretty complex problem. I suggest you look at the PHP Design
Patterns book to see how they treat the Singleton. I haven't had a chance yet
myself, and for this example, it is very difficult to test. You would need to look at
the instance info like you have above and monitor the database connection to
see that it does persist and is not duplicated.
In the meantime, I can tell you that holding the object in the session is a good
attempt to overcome state, but it also writes to disk and may give you no better
performance than creating a new connection with each page.
Also, part of what Cully was suggesting might be to not implement a true
Singleton pattern, but to check for the object instance in the script or session
before instantiating any new ones. Of course that is the sort of thing that
typically takes place in a Singleton class constructor.
The last thing is about the error message you were getting... The one about
an incomplete object is basically saying you have to instantiate an empty object
locally (current script) before you can assign another object, say from the session,
to it.
I know this must feel like coding in circles. Sorry I can't be of more help.
--brian
Typically, though, the Singleton class can handle the second instantiation attempt
and basically just ignore it.
Anyway, this is a pretty complex problem. I suggest you look at the PHP Design
Patterns book to see how they treat the Singleton. I haven't had a chance yet
myself, and for this example, it is very difficult to test. You would need to look at
the instance info like you have above and monitor the database connection to
see that it does persist and is not duplicated.
In the meantime, I can tell you that holding the object in the session is a good
attempt to overcome state, but it also writes to disk and may give you no better
performance than creating a new connection with each page.
Also, part of what Cully was suggesting might be to not implement a true
Singleton pattern, but to check for the object instance in the script or session
before instantiating any new ones. Of course that is the sort of thing that
typically takes place in a Singleton class constructor.
The last thing is about the error message you were getting... The one about
an incomplete object is basically saying you have to instantiate an empty object
locally (current script) before you can assign another object, say from the session,
to it.
I know this must feel like coding in circles. Sorry I can't be of more help.
--brian
If you're just trying to get a persistent sql connection, use a function like mysql_pconnect().
Cully
Cully
http://www.php.net/manual/en/ref.session.php
If the instance doesn't exist, check for it in the session.
Cully