动态重新定义PHP类函数?

6

我正在尝试弄清楚如何在运行时导入大量的PHP类函数。例如...

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!";
        }
    }

我发现了几种可能的方法,例如classkit_method_redefine()runkit_method_redefine()(它们是“实验性”的,并且无法修改当前运行的类)。 我现在使用的是PHP 5.3.3,所以我不能使用Traits(不确定那是否是我要找的东西)。 我已经成功地重新定义了处理程序变量,像这样:

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

$foo = new Entity();

但是,这感觉像是一种非常hacky的方法。更重要的是,如果我不命名每个类$foo的实例,那么它将无法工作。有没有什么解决方法可以达到我想做的事情?
注意:我知道我可以扩展一个类,但在我的情况下,当Entity类被初始化时,没有安全的方法预先知道它需要用哪个子类来初始化。也许我可以编写一个方法,例如:
public function changeClass
    {
    this->class = OtherEntity;
    }

感谢您的帮助!

2
你的实际问题肯定有另一个解决方案。你的第一个例子建议使用工厂模式,但我不确定你真正想要实现什么。 - VolkerK
2
是的,你做了一些设计选择需要重新考虑。 - Marcel Burkhard
@CodeGodie 不是,仅为示例目的。 - DarthCaniac
不要这样做。在基类中定义常见模式并进行扩展。做一些研究 :) - Gogol
1
我能理解。被接受的答案对于这种情况是完美的。 :) - Gogol
显示剩余2条评论
2个回答

5
这里有一个可以尝试的可能解决方案。让和类扩展类。不过,类将使用一个工厂根据数值是否安全来实例化对象。让我们详细地看一下:
/*
 * 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!";
    }

}

现在要使用这个方法,调用工厂方法,如果一切正常,它将实例化适当的类,该类将扩展Entity
$person = Entity::factory("Human");
$person->sayHi();

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

使用is_subclass_of()将确保安全,因为如果传入的值不是扩展Entity的类,您将返回FALSE
如果您想看到上面的代码实际运行,请复制上面的php代码并在phpfiddle.org上进行测试。

你真是太棒了。谢谢!这正是我在寻找的。 为什么要抽象地定义sayHi();函数?即使它没有在原始的Entity类中定义,它似乎也能工作。 - DarthCaniac
2
@DarthCaniac 这是正确的。 它绝对会这样做。 abstract 关键字的好处是强制任何扩展带有 abstract function 的基类的类在其自己的类中实现该方法。 换句话说, HumanCow 都必须实现 sayHi() ,否则php解释器将抛出错误。 这保证了任何扩展 Entity 的类都包含一个有效的 sayHi() 方法,可以针对该类执行特定操作。 - War10ck
非常抱歉。这条评论是针对原帖的。移动软件很糟糕。 - Gogol
1
哈哈,没关系。我也猜到了。只是想确认一下,看看你有没有推荐的编辑建议。 - War10ck
实际上,你建议的是我本来就会做的。抽象类就是为此而生的。我会点赞的,谢谢 :) - Gogol

1
你可以创建 HumanCow 作为 Entity 的子类之一。当你使用 new Entity("Human") 时,你可以将一个新创建的 Human 对象存储在 Entity 实例中。

然后你可以使用 __call 将方法调用重定向到 "子元素"。

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();

唯一的缺点是$person$cow都是Entity对象。

1
进一步阅读:这将是一种 对象组合 的形式。 - VolkerK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接