6

I am trying to figure out how to import a large number of PHP class functions on the fly. For example...

class Entity
{
    public function __construct($type)
    {
    require_once $type."_functions.php"
    }

    // ...

}

$person = new Entity("human");
$person->sayhi();
$cow = new Entity("cow");
$cow->sayhi();

human_functions.php:

class Entity redefines Entity
    {
    public function sayhi()
        {
        echo "Hello world!";
        }
    }

cow_functions.php:

class Entity redefines Entity
    {
    public function sayhi()
        {
        echo "Moo!";
        }
    }

I have found a few possibilities like classkit_method_redefine() and runkit_method_redefine() (which are "experimental", and they cannot modify the currently running class anyway). I am on PHP 5.3.3 right now, so I can't use Traits (Not sure if that is what I am looking for anyways). I have had success redefining the handler variable like this:

// Example 2:
class OtherEntity { /* Code Here */ }
class Entity
    {
    public function __construct($type)
        {
        global $foo;
        unset($foo);
        $foo = new OtherEntity();
        }
    }

$foo = new Entity();

But, this feels like a very hacky method. More importantly, if I don't name every instance of the class $foo, then it will not work. Are there any workarounds for what I am trying to do?

Note: I am aware that I can extend a class, but in my case when the Entity class is initiated, there is no safe way to know in advance what subclass it would need to be initiated with. Perhaps there is a method I could write, such as:

public function changeClass
    {
    this->class = OtherEntity;
    }

Thanks for your help!

DarthCaniac
  • 383
  • 5
  • 16
  • 2
    There's defintely another solution to your actual problem. Your first example suggest the [factory pattern](http://www.tutorialspoint.com/design_pattern/factory_pattern.htm), but I'm not sure about what you really want to achieve. – VolkerK Oct 07 '15 at 17:23
  • 2
    Yeah you made some design choices you need to rethink. – Marcel Burkhard Oct 07 '15 at 17:23
  • is `redefine` an actual php function. I believe you're looking for `extends` – CodeGodie Oct 07 '15 at 17:26
  • @CodeGodie No it is not, it is for example purposes – DarthCaniac Oct 07 '15 at 17:27
  • Don't do it like this. Define common patterns in base class and extend it. Do some research :) – Gogol Oct 07 '15 at 18:46
  • @noc2spamツ "I am aware that I can extend a class, but in my case when the Entity class is initiated, there is no safe way to know in advance what subclass it would need to be initiated with." – DarthCaniac Oct 07 '15 at 18:48
  • 1
    I can understand. The accepted answer is perfect for this scenario. :) – Gogol Oct 07 '15 at 18:50

2 Answers2

5

Here's an idea of a possible solution you could try. Let the Cow and Human classes extend the Entity class. However, the Entity class would use a factory to instantiate the objects based on if the value was safe. Let's look at this in more detail:

/*
 * Class Entity should not be able to be instantiated.
 * It should contain a factory to instantiate the
 * appropriate entity and an abstract function declaring 
 * the method that each entity will need to implement.
 */
abstract class Entity {

    public static function factory($type) {
        return (is_subclass_of($type, "Entity")) ? new $type() : FALSE;
    }

    abstract public function sayHi();

}

/*
 * Human class extends Entity and implements the
 * abstract method from Entity.
 */
class Human extends Entity {

    public function sayHi() {
        echo "Hello World!";
    }

}

/*
 * Cow class extends Entity and implements the
 * abstract method from Entity.
 */
class Cow extends Entity {

    public function sayHi() {
        echo "Moo!";
    }

}

Now to use this method, call the factory method and if all works well, it'll instantiate the proper class which will extend Entity.

$person = Entity::factory("Human");
$person->sayHi();

$cow = Entity::factory("Cow");
$cow->sayHi();

Using, is_subclass_of() will keep you safe because if the passed in value is not a class that extends Entity, you'll be returned a value of FALSE.

If you'd like to see the above code in action, copy the above php code and test it out on phpfiddle.org.

War10ck
  • 12,387
  • 7
  • 41
  • 54
  • You're the man. Thank you! This is exactly what I was looking for. Why do you abstractly define the ```sayHi();``` function? It seems to work even if it is not defined in the original ```Entity``` class.. – DarthCaniac Oct 07 '15 at 18:08
  • 2
    @DarthCaniac That's correct. It absolutely will. The benefit of the `abstract` keyword forces any classes that extend a base class with an `abstract function` to implement that method within their own class. In other words, both `Human` and `Cow` have to implement `sayHi()` or the php interpreter will throw an error. This guarrantees that any class that extends `Entity` will contain a valid `sayHi()` method that can do something specific for that class. – War10ck Oct 07 '15 at 18:10
  • Lol very sorry. This comment was meant for the op. Mobile sof sucks lol. – Gogol Oct 07 '15 at 18:45
  • 1
    Haha, no worries. I figured as much. Just wanted to check and see if you had a recommended edit. – War10ck Oct 07 '15 at 18:46
  • Actually, what you have suggested is what I would have done anyway. Abstract classes are made for this. I will upvote thanks :) – Gogol Oct 07 '15 at 18:48
1

One thing you can do is create Human and Cow as subclasses of Entity. When you do new Entity("Human"), you can store a newly created Human object inside the Entity instance.

Then you can use __call to redirect method calls to the "child element".

class Entity{
    private $child;

    public function __construct($type){
        $this->child = new $type;
    }

    public function __call($func, $params=array()){
        $method = method_exists($this, $func)
            ? [$this, $func] : [$this->child, $func];
        return call_user_func_array($method, $params);
    }
}

class Human extends Entity{
    public function __construct(){}

    public function sayhi(){
        echo "Hello world!";
    }
}

class Cow extends Entity{
    public function __construct(){}

    public function sayhi(){    
        echo "Moo!";
    }
}

$person = new Entity("Human");
$person->sayhi();

$cow = new Entity("Cow");
$cow->sayhi();

The only downside is that $person and $cow are both Entity objects.

gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • 1
    For further read-up: That would be one form of [object composition](https://en.wikipedia.org/wiki/Object_composition). – VolkerK Oct 07 '15 at 17:32