views:

42

answers:

3

Looking to see if there is any way to implement the kind of functionality of the __get() magic method, but when trying to instantiate a new class.

My goal is to have

$foo = new Cat();

Ultimately result in the creation of a generic object, i.e.

new Mammal('Cat');

Such that $foo would be an instance of the class Mammal, with the called class name ('Cat') passed as an argument to Mammal's constructor.


Note: The end game I have in mind, for those familiar with Lithium or CakePHP, is to avoid having to define a whole bunch of classes for each different database table. If I did, most would simply be empty, the basic CRUD operations being all that is necessary. Plus, all those includes and class definitions can't be great for overhead. My idea would be to use a single "Model" class to handle most of the generic functions, and I could always create subclasses if I needed more advanced functionality for a specific database table.

+2  A: 

I don't think there is any straightforward non-hacky way to achieve what you want with the class instantiation mechanism.

I would recommend using a factory:

$tableFactory->createTable('Cat');

Which would then work out what needs to be done behind the scenes. This also has the benefit of if you ever decide Cat requires its own class, you can simply modify the factory and you may not need to modify the code that is using it.

Jani Hartikainen
This would definitely work, and may be my only solution. I kinda wanted to be able to just write "new Cat();" for readability, but it isn't essential. It would be nice though ...
Dave W.
Should probably be static - e.g. `TableFactory::createTable('Cat');`. Otherwise good answer, factory pattern is the one to use here.
Pete
Static or non-static largely depends on your style. If you need dependency injection or other such things, non-static is probably better. Static is simpler to implement though.
Jani Hartikainen
A: 

Well, it's a bit hacky, but you could abuse the class autoloader...

So, hijack the loader to check for a class "Cat", if it doesn't exist, then eval it into existance...

if (!$foundClass) {

    $code = 'Class '.$class.' extends Mammal { 
        public function __construct() { parent::__construct("'.$class.'");}
    }';
    eval($code);
}

I said it was hacky... But you could always white-list the classes you want to do this for... Plus, it has the benefit that if you wanted to modify the class, just create a firm instance of it.

But then again, I would personally find another solution. To me, new Cat() is not readable at all. That's because of two reasons, first there's no contextual clues as to what it is and second I can't find the class Cat... What I would do is similar to what Jani suggested: $table = DatabaseTable::getInstance('cat');... I find that MUCH more readable (even though it is more characters)...

ircmaxell
A: 

The way I remember it from Cake is that you define the model on top of the controller class and then simply use $this->Modelname. This should be as simple to implement as:

public function __get($prop)
{
    if(in_array($prop, $this->uses)) {
        return new $prop;
    }
}

Each time you call a non-existing property, your class would check if the property name exists in an array $uses and if so, assumes $prop is classname and instaniates it. You will want to store the instance somewhere to avoid reinstantiating each time you fetch it.

This is somewhat cleaner than writing new Klass all over the place, because that makes it hard to exchange Klass for something else. You hardwire it into the controller then. It's a dependency you want to avoid. With that said, you might want to have a look at the Symfony Dependency Injection framework.

Gordon