Semi Singleton

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Semi Singleton

Post by Benjamin »

How can I make a class that populates a variable with the first instance of itself? I want to be able to call the class so that I can retrieve the first instantiated instance..

Code: Select all

class whatsupyo {
    private static $instance = false;

    function __construct() {
        if ($instance === false) {
            $instance = ?
        }
    }

    function get_instance() {
        return $instance;
    }
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

There's something fishy with doing this but..

Code: Select all

class foo
{
  private static $instance = null;

  public function __construct()
  {
    if (self::$instance === null)
    {
      self::$instance = $this;
    }
    else
    {
      throw new SingletonException();
    }
  }

  public static function GetInstance()
  {
    if (self::$instance === null)
    {
      $class = __CLASS__;
      new $class();
    }
    return self::$instance;
  }
}
edit: possible logic fix.
Last edited by feyd on Sat Aug 19, 2006 11:30 am, edited 1 time in total.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

Why not lazy load the in the getInstance method instead?

That way, no matter what calls it first, aslong as they call the getInstance method you'll always have a singleton?

in other words, I don't see what this will achieve.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Post by Benjamin »

I don't want it to be a singleton, but I want to be able to retrieve the first instance without using a registry or global variable. Hence I figured this would work. Works great using $this.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

astions wrote:I want to be able to retrieve the first instance without using a registry or global variable.
That's the general definition of a singleton.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Post by Benjamin »

I also need to be able to instantiate new instances of it, although, come to think of it, maybe it would be better to make copies of it.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

I don't like the concept, but this is what you are looking for.

Code: Select all

class foo
{
  private static $instance = null;
  protected $bar;

  public function __construct()
  {
    if (self::$instance === null)
    {
      self::$instance = $this;
    }
    $this->bar = mt_rand(1,100);
  }

  public static function GetFirstInstance()
  {
    if (self::$instance === null)
    {
      $class = new ReflectionClass(__CLASS__);
      $class->newInstance();
    }
    return self::$instance;
  }
}

$foo1 = new foo();
$foo2 = new foo();
$foo3 = foo::GetFirstInstance();
var_dump($foo1 === $foo2, $foo2 === $foo3, $foo1 === $foo3, $foo1, $foo2, $foo3);
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Post by Benjamin »

feyd, I was in the process of posting this while you posted your example. I'll use your example, but can you tell me why this doesn't work?

Code: Select all

class test {
    private static $instance = false;

    function __construct() {
        if (self::$instance === false) {
            self::$instance = $this;
        }
    }

    public static function getInstance(){
        return self::$instance;
    }
}

$activate = new test();

// and I try to get the instance with..
$test = test::getInstance();
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

It appears to work for me.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

I don't understand the reason for putting the singleton logic in the constructor. Eeven if you had that same logic in the factory method for it you'd essentially have the same thing.

~astions, have you looked at using a registry? That would give you far more flexibility and allow to store mutliple instances if you wanted to, whilst still be able to refer to an instance over and over like a singleton without needing to pass it from object to object at each step.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Post by Benjamin »

It's in the constructor to ensure that the first instance is captured.

As far as a registry is concerned, I've considered that, but with this method, which might just very well be a new pattern, I avoid using a registry, I can get an instance of the class from any other class without using globals, and I can still create a new instance if I need to.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

astions wrote:I can get an instance of the class from any other class without using globals, and I can still create a new instance if I need to.
I'd change your singleton factory to this:

Code: Select all

public static function getInstance(){
        if (self::$instance === null) self::$instance = new test();
        return self::$instance;
    }
Otherwise, how can you be sure you'll return a singleton? For your code to work you need to already have a live instance before the singleton will be returned ;)
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Post by Benjamin »

Yeah I was going to do that, Thanks for the good example though, now I can just plug it in :)
Post Reply