tags:

views:

67

answers:

3

Hello, I have a question I was wondering for a while, often when I find scrips for php or look at php frameworks I see a "registry class" or a "container class" which often holds variables or other objects utilizing the __get magic method.

Here is a oversimplified example of what I mean:

example 1:

class container {
 private $objects;
 public function __get($class){
  if(isset($this->objects[$class])){
   return $this->objects[$class];
  }
  return $this->objects[$class] = new $class();
 }
}

the above example will have more functions to it when creating the class instead of just calling it but for my example it should be enough. "example 1" is how I mostly see it in scripts downloaded from the internet, it maintains a single class instance, now what I'm wondering is that wouldn't this example do the same thing and be more efficient:

example 2:

class simplecontainer {
 public function __get($class){
  return $this->$class = new $class();
 }
}

But I never see "example 2" in other peoples scripts which makes me think twice before even considering to use it.

I tested container vs simplecontainer using several classes that they would contain and re-use around 100000 times and "example 1" does it in 0.75seconds on my local machine, and "example 2" does it in 0.29seconds.

So which should I use in my scripts? example 1 or example 2? and why?

thanks Mike and prodigitalson, you both deserve best answer here for approaching different issues with example 2.

edit: for example 2 to work like example 1 you MUST avoid defining any private or protected variables in simplecontainer and you need to make sure to never directly set any variables on the simplecontainer also there might be more quirks that have to do with it.

edit2: after further testing I have found out that in a real world full size application "example 2" provides no significant benefit to performance, with a little tweaking of example 1, example 2 is actually slower. I hope this provides some insight to someone other than me why example 2 is never used even though it is simpler looking.

A: 

example 2 is faster because it's not a registry class it's only a 'mask' for another class. So it's really doesn't do anything.

Some info about registry patterns.

Edit:

While example one holding added objects in an array in example two you always overwrite your 'registry' which is nothing more but a single variable that holds exactly one object.

fabrik
how doesn't it do anything if the result for both examples is pretty much the same?? please explain. It stores the instance of the class? how is that doing nothing?
YuriKolovsky
what you are saying does not make sense to me :S
YuriKolovsky
I'm sorry dude you **don't want** to understand how the registry pattern works so i cannot help you. My first comment was showed what's your problem here, i've provided some answers, links to shed light what's your misunderstandings here with no success. If you don't want to understand i don't want to help you.
fabrik
fabrik I have read about registry patters a long time ago, and have fiddled with them for a long time, I just thought the "on demand" method of just storing the variable that gets called only when it gets called the first time made more sense than having a function check for its presence each time, but what you are saying is wrong, it does do something, almost the same thing as example 1.
YuriKolovsky
No. You even don't understand it's fundamentals.
fabrik
you try too hard to prove that I am wrong without understanding or answering my question.
YuriKolovsky
@fabrik the code is not overwriting the objects. Remember __get is triggered when a the member does not exist. If you do $this->foo, the second example will create a public member foo. Subsequent calls to $this->foo will not trigger __get anymore, because now there is a member of that name.
Gordon
@YuriKolovsky: I don't want to defend or attack anyone (I really just want you two to stop fighting), but as far as I see it, I'm guessing fabrik is just frustrated that 'Avoid global data!' in his link, very near the start of the article, doesn't seem to have caught your attention. It might have, of course. But if it did, I don't think that's clear.
pinkgothic
This comment discussion is way to long but i guess you just overlooked the $ at $this->$class = new .. So $foo->x will get you an "x" object that is stored in $this->x and $foo->y will get you one that is stored in "this->y"
edorian
I have been pointed at not using global data even before I used it, I read the article until the part with singletons, I'm not interested in singletons because I consider them still to be global data, what I am interested in is simply a simpler way to access existing objects, but I'm not sure anything I say is clearing anything up.
YuriKolovsky
@edorian yes that's the point, both examples do pretty much the same, except that example 2 is a bit more error prone because it does not contain the objects in a private variable.
YuriKolovsky
@YuriKolovsky That is exactly what i was trying to say, all well :)
edorian
+3  A: 

Without first off example2 doesnt define the objects as protected or private which means that simplecontainer::objects will be a public member that can be overwritten like:

$container->className = new SomethingElse();

prodigitalson
Wait, I thought that after creating the instance and storing it in a variable by doing this $this->$class = new $class(); it gets stored and it will no longer call __get on any subsequent calls? I will test this right away.
YuriKolovsky
yes, im correct, it only gets called when creating the object and it stores it for the future ones, so your second point is wrong.
YuriKolovsky
Yeah youre right... i overlooked the fact that it wasnt storing them in an `objects` member... ill edit. good catch.
prodigitalson
@Yuri: you don't understand yet.
fabrik
so the reason they use example 1 is just for a safeguard against people rewriting the object?
YuriKolovsky
@YuriKolovsky: Yes. That's the point of a Registry. See the http://www.phppatterns.com/docs/design/the_registry link fabrik linked to.
pinkgothic
And here we go again. An accepted answer while OP didn't learn anything. Then later today or tomorrow he will posting the same question over and over again. :(
fabrik
and why do you think that?
YuriKolovsky
You've constantly rave about registry classes but it's not my business. You'll use them how you want.
fabrik
thank you for understanding.
YuriKolovsky
@prodigitalson the ability to overwrite a contained object just like that might be desirable if I would need a ability to "reset" the object variable, but obviously this could get very confusing very fast.
YuriKolovsky
@yuri: but then you have no assurance that the new object/var will be an instance of `$class`.. something that could be mistakenly depended on by an assumed convention that doesnt exist :-) kind of goes to Mike's point in the marked answer.
prodigitalson
+4  A: 

Because in yours I can't do this:

class container {
    private $foo = "I'm only a var";
    private $objects;
    public function __get($class){
        if(isset($this->objects[$class])){
            return $this->objects[$class];
        }
        return $this->objects[$class] = new $class();
    }
}

class simplecontainer {
    private $foo = "I'm only a var";
    public function __get($class){
        return $this->$class = new $class();
    }
}

class foo {
    public $bar = "wibble";
}

$c1 = new container();
$c2 = new simplecontainer();

$foo = $c1->foo;
$foo->bar = "Don't you forget about me!";
unset($foo);
$foo = $c1->foo;
echo $foo->bar;

$foo = $c2->foo;
$foo->bar = "Don't you forget about me!";
unset($foo);   
$foo = $c2->foo;
echo $foo->bar;
// Doh!

Your version relies on no class name being the same as a private member of the container object. Ok, so it should be simple to avoid, but would be a pain tracking down a bug for. Safe coding means less stress.

Mike
im still thinking this through, but hmmm, I like less stress :D
YuriKolovsky
It actually throws up some weird behaviour from PHP (no surprise). The simplecontainer in my example returns one `foo` object to the caller and then sets an entirely different one as the private method in the container. Seeing as how objects are meant to get passed by reference it's a bit wacky.
Mike
the unset() in your example seems like it does nothing? well, nothing to change the outcome at least.
YuriKolovsky
yes, this is of great insight into the quirkyness of php xD, took me a while to understand the differences but is invaluable for me at least. Thank you for helping me fix my confusion!!
YuriKolovsky
Had something in my head about clearing references when I wrote it. As you say, they're totally unnecessary.
Mike
I do agree with fabrik though, this isn't a registry.
Mike
I never said it was :) I only said that I often see objects stored in classes with registries or containers in a way that seemed inefficient to me.
YuriKolovsky