11

I am working with PHP classes and objects now. In this question the names of fields and methods are made up just so you get an idea of what I am talking about.

It is related to using the singleton and registry design patterns.

Now lets say I need to access a Database object, Cache Object, Core Settings object, Session object in almost every other class I will need to have access to these. SO I would use a registry to store all 4 of those object into 1 registry class object. I could then easiyl just pass in my 1 object into any other object that needs to access these. So that sounds great so far but what if I have some classes that do not need all 4 of those objects, what If I ONLY need to access the Database object or the Session object in some of my other classes? For perfromance would it be best to just use a singleton inside these other objects or would it be the same to go ahead and use my registry in these?

I do not know well enough how the objects work in PHP to know if there would be any sort of performnce gain (less memory usage, cpu usage, load time).

So anyone with experience in this maybe can tell me if there would be any gain using one or the other, I am at the stage where I can go ether way without it affecting production time or anything. I would like to use the best method if I can now.

JasonDavis
  • 48,204
  • 100
  • 318
  • 537
  • I actually found a question just now that is basicly the same http://stackoverflow.com/questions/1967363/registry-or-singleton – JasonDavis Jan 20 '10 at 14:24
  • Take note if you're using PHP4 or PHP5... objects work very differently, and in PHP4 keywords like 'static' aren't supported. Chances are you're using 5, but if you want to get into relevant OOP you need to be sure. – Richard JP Le Guen Jan 20 '10 at 14:30

3 Answers3

4

You can implement lazy loading to only load the objects you really need:

class Registry
{
    private static $database = null;

    private static function connectDatabase($key)
    {
        [... do connection stuff ...]
    }

    public static function getDatabase($key)
    {
        if (Registry::$database == null)
        {
            Registry::connectDatabase($key);
        }
        return Registry::$database;
    }
}

The code to register the database connection parameters is left as exercise to the reader.

4

That depends on your application. If you still need 3 out of the 4 classes, then it'd be more ideal to use the Registry than to handle the 3 independently only because you don't need the fourth. Loading the classes lazily would be one approach to reduce memory footprint, but then you need to instruct the registry when to create the objects and that's not much different than handling singletons. Alternatively, you could create an n-parameter constructor or use an array to instruct your Registry which classes to instantiate during construction.

class Registry {
    public $class1;
    public $class2;

    function __construct($uses) {
        foreach($uses as $class) {
            $this->{$class} = new {$class}();
        }
    }

}

Then instantiate your Registry by specifying which classes to instantiate.

$reg = new Registry(array('class1'));

You would obviously want your constructor to handle zero parameters to account for instantiating all classes by default.

Bret Kuhns
  • 4,034
  • 5
  • 31
  • 43
  • Thats a cool idea, thanks. I should mention, even if 1 class only needs 1-2 of the objects, there is already another class being called on the same page most likely that will also have all 4 objects already injected into it. I am not sure how the memory stuff weorks though so it may be already loaded 1 time. I don't know if the memory would store the objects more then once in ram or not. – JasonDavis Jan 20 '10 at 14:38
  • My code example assumes your classes hold onto their own instance of your Registry. This would mean lots of excess memory if you have more than one class per page utilizing the same common Registry objects. If you're using a static/global instance, then your implementation would be different than my example but it could still be done. This would also mean sharing the same objects in memory across all classes, which would be much more efficient. To get my example to work that way, make a Registry::initInstances() function instead of using the constructor. – Bret Kuhns Jan 20 '10 at 14:53
  • Ok thats what I was thinking, guess we are on the same page, kind of like the method from dbemerlin below. – JasonDavis Jan 20 '10 at 17:40
  • Where is the Registry Principle ? I still don't see the ~proper implementation. Still don't get the point why this post is accepted as correct answer. – Valentin Rusk Jan 13 '14 at 16:12
  • @ValentinRusk what don't you see? http://www.martinfowler.com/eaaCatalog/registry.html Fowler's definition of `Registry` is fairly simple and I feel my answer provides an interpretation. Instead of `getClass1()` or `getClass2()` functions, my snippet provides them as public members. If you need more complicated logic, such as lazy loading, then isolate the object instances behind member functions. Otherwise, the pattern is the same. – Bret Kuhns Jan 14 '14 at 02:39
  • Oh, Yeah. Martin Fowler was wrong implementing registry this way. I dont see any point using registry this way. Why do you need the object in this case? use array, the access will be also faster. No matter, i won't argue about it. Classic Registry needs to contain getter, setter and store an instances encapsulated. In this case it is not encapsulated at all and this is going against oop concepts (encapsulation). – Valentin Rusk Jan 15 '14 at 15:50
1

Perhaps this is the proper Singleton-Registry pattern. OFC, you can implement different things, SplFixedArray, ArrayAccess interface, and others. Also it is not bad to add the destruct and destroy inner objects to ensure no leak possible.

class oRegistry{
   private static $instance = null;

   private $storage = array();

   private function __construct(){}

   private function __clone(){}

   public static function getInstance(){
      if( self::$instance === null ){
         self::$instance = new self();
      }
      return self::$instance;
   }

   public function attach($name, $o) {
      if( true === isset($this->storage[$name]) ) {
          throw new Exception('The instance with name '.$name.' already exists in registry.');
      }
      if( !empty( $name ) ) {
          $this->storage[ $name ] = $o;
      }
   }

   public function detach( $name ){
       if( isset( $this->storage[ $name ] ) ) {
           $this->storage[ $name ] = null;
           unset( $this->storage[ $name ] );
       }
   }

   public function get( $name ){
       if( false === isset( $this->storage[$name] ) ) {
           throw new Exception('Invalid instance requested');
       }
       return $this->storage[ $name ];
   }
}

// usage example
$storage = oRegistry::getInstance();
$obj = new stdClass;
$obj2 = new stdClass;
$obj->test1 = 'test';
$obj2->test2 = 't2';

$storage->attach( 'test1', $obj );
$storage->attach( 'test2', $obj2 );

$got = $storage->get( 'test2' );
var_dump($got); // object(stdClass)#3 (1) { ["test2"]=> string(2) "t2" }

$storage->detach( 'test2' );
$got = $storage->get( 'test2' );
var_dump($got); // bool(false)
Valentin Rusk
  • 630
  • 5
  • 13