Singletons and Registry

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Singletons and Registry

Post by John Cartwright »

Ok, I'm having a lot of troubles understanding how to implement a singleton pattern since it is vaguely documented.. :( Anyways over at http://www.phppatterns.com/docs/design/the_registry

Code: Select all

class Singleton
{
    function Singleton() {
        $registry = &Registry::instance();
        if ($registry->isEntry('singleton ' . get_class($this))) {
            trigger_error(
                    'Already an instance of singleton ' .
                    get_class($this));
        }
    }
	 
    function &instance($class) {
        $registry = &Registry::instance();
        if (!$registry->isEntry('singleton ' . $class)) {
            $registry->setEntry(
                    'singleton ' . $class, new $class());
        }
        return $registry->getEntry('singleton ' . $class);
    }
}
and

Code: Select all

class Registry 
{
    var $_cache;
    
    function Registry() {
        $this->_cache = array();
    }
    function setEntry($key, &$item) {
        $this->_cache[$key] = &$item;
    }
    function &getEntry($key) {
        return $this->_cache[$key];
    }
    function isEntry($key) {
        return ($this->getEntry($key) !== null);
    }
    function &instance() {
        static $registry;
        if (!$registry) {
            $registry = new Registry();
        }
        return $registry;
    }
}
Can anyone point me in the right direction on how to maintain a single instance of any particular class through my whole application?

Lets use my httpRequest class as an example.. from what I've gotten out of it all I have to do is extend the singleton and call $this->singleton() in the constructor to input the instance into cache.. apparantly that's not right?

Code: Select all

class httpRequest extends Singleton
{
	var $post   = array();
	var $get    = array();
	var $cookie = array();
	
	function httpRequest() {
	    $this->Singleton();
		$this->get    = Sanitizer::sanitize($_GET);
		$this->post   = Sanitizer::sanitize($_POST);
		$this->cookie = Sanitizer::sanitize($_COOKIE);
	}
User avatar
neophyte
DevNet Resident
Posts: 1537
Joined: Tue Jan 20, 2004 4:58 pm
Location: Minnesota

Post by neophyte »

What are you trying to put in the registry? Where are your singleton/registry calls? The test case calls for them to be instansiated like so:

Code: Select all

function testRegistrySaveAndRestore() {
        $registry = &Registry::instance();
        $singleton = &ExampleSingleton::instance();
At some point you still have to instantiate them. Then you can add them to the registry with something like:

Code: Select all

$registry->setEntry('key', $objecttostore);
Correct me if I'm wrong. :wink: I've only worked through the registry code. I'm writing my own examples.

I've only worked through the first registry scenario like this:

Code: Select all

class Registry {
    var $_cache;

    function Registry() {
        $this->_cache = array();
    }
    function setEntry($key, &$item) {
        $this->_cache[$key] = &$item;
    }
    function &getEntry($key) {
        return $this->_cache[$key];
    }
    function isEntry($key) {
        return ($this->getEntry($key) !== null);
    }
}
class mog{
    var $mogstr;
    function mog($str){
        $this->mogstr = $str;
    }

}
$mog = &new mog('Part man and part dog: a mog!');
$registry = &new Registry();
$registry -> setEntry( 'mog', &$mog);

class whatever{
   function whatever(&$registry){
      $returned = $registry -> getEntry('newprint');
      $returnmog = $registry -> getEntry('mog');
      echo $returnmog -> mogstr;
   }
}
$what = new whatever($registry);
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

ahh that makes things much clearer.. for some reason I though the class name was given through get_class() but that doesn't make sense the way it is setup. Thats what you get for theorizing :x

Just realized, at what point does the singleton come into play...
User avatar
neophyte
DevNet Resident
Posts: 1537
Joined: Tue Jan 20, 2004 4:58 pm
Location: Minnesota

Post by neophyte »

Here's a working example I built. I'm sure there's more you can do here...

Code: Select all

class Registry {
    var $_cache_stack;

    function Registry() {
        $this->_cache_stack = array(array());
    }
    function setEntry($key, &$item) {
        $this->_cache_stack[0][$key] = &$item;
    }
    function &getEntry($key) {
        return $this->_cache_stack[0][$key];
    }
    function isEntry($key) {
        return ($this->getEntry($key) !== null);
    }
    function &instance() {
        static $registry = false;
        if (!$registry) {
            $registry = new Registry();
        }
        return $registry;
    }
    function save() {
        array_unshift($this->_cache_stack, array());
        if (!count($this->_cache_stack)) {
            trigger_error('Registry lost');
        }
    }
    function restore() {
        array_shift($this->_cache_stack);
    }
}
class Singleton {
    function Singleton() {
        $registry = &Registry::instance();
        if ($registry->isEntry('singleton ' . get_class($this))) {
            trigger_error(
                    'Already an instance of singleton ' .
                    get_class($this));
        }
    }
    function &instance($class) {
        $registry = &Registry::instance();
        if (!$registry->isEntry('singleton ' . $class)) {
            $registry->setEntry(
                    'singleton ' . $class, new $class());
        }
        return $registry->getEntry('singleton ' . $class);
    }
}
class mog{
    var $mogstr;
    function mogstr($str){
        $this->mogstr = $str;
    }

}
class whatever{
   function whatever(){
  $registry = &Registry::instance();
  $mog = &Singleton::instance('mog');
        $mog->mogstr('I am a mog part man, part dog.');
        echo $mog -> mogstr;
   }
}
$whatever = new whatever;
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Ah cool very helpful and thank you.
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Code: Select all

class whatever{
   function whatever(){
  $registry = &Registry::instance();
  $mog = &Singleton::instance('mog');
        $mog->mogstr('I am a mog part man, part dog.');
        echo $mog -> mogstr;
   }
}
FYI.. no need to set $registry instance in your example since the static call to instance in Singleton does it for you[/img]
User avatar
neophyte
DevNet Resident
Posts: 1537
Joined: Tue Jan 20, 2004 4:58 pm
Location: Minnesota

Post by neophyte »

Interesting. I'm sure there is more for me to discover about this very useful pattern. Anybody who has worked with OOP before in PHP knows what a pain it is to have to pass objects around by reference or create new ones in classes. This is so much easier!
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Indeed, I've probably refactored atleast 15 classes in my framework now.. and loving it.
Post Reply