Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redefining PHP class functions on the fly?

Tags:

oop

php

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!

like image 904
DarthCaniac Avatar asked Oct 07 '15 17:10

DarthCaniac


2 Answers

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.

like image 188
War10ck Avatar answered Nov 09 '22 18:11

War10ck


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.

like image 26
Rocket Hazmat Avatar answered Nov 09 '22 17:11

Rocket Hazmat