Link to home
Start Free TrialLog in
Avatar of abstractionz
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::getInstance();
$singleton->displayMessage();// display message

$singleton2=Singleton::getInstance();
$singleton2->displayMessage();// display message

print "$singleton<br />";
print "$singleton2<br />";


      
?>
==========

test2.php
==========

require_once "singleton.php";

$e = new Exception();

$singleton=Singleton::getInstance();
$singleton->displayMessage();// display message

$singleton2=Singleton::getInstance();
$singleton2->displayMessage();// 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??


ASKER CERTIFIED SOLUTION
Avatar of Brian Bush
Brian Bush
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Avatar of Roonaan
Roonaan
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of cully_larson
cully_larson

Another solution would be to store your instance in a Session variable:

http://www.php.net/manual/en/ref.session.php

If the instance doesn't exist, check for it in the session.


Cully
Avatar of abstractionz

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
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
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
If you're just trying to get a persistent sql connection, use a function like mysql_pconnect().

Cully