【问题标题】:PHP - dynamically extend parent class with static methodsPHP - 使用静态方法动态扩展父类
【发布时间】:2015-03-05 13:56:15
【问题描述】:

考虑以下几点:

class A
{
    public static function bark()
    { echo 'woof'; }
}

class B extends A
{
    public static function speak()
    { echo 'hello'; }
}

A::speak();

// Fatal error: Call to undefined method A::speak()

应该如何使用您需要在该类中全局可用的方法扩展一个类,该类中的方法尚不知道,但在运行时加载,具体取决于您的应用程序的流程?

是的,我们可以创建traits 并将use 放入类中,例如:

trait B
{
    public static function speak()
    { echo 'hello'; }
}

class A
{
    use B;
    public static function bark()
    { echo 'woof'; }
}

A::speak();

// hello
  • 但是use B 不会被动态调用,因此您必须使用每个可用的新特征手动更新 A 类。 这太荒谬了,为什么要强迫开发人员打破他们的大脑来尝试完成如此简单的事情?

有人知道如何以干净的方式完成此操作吗?我的意思是我通过使用单例、命名空间、回调和作品看到了一些令人印象深刻的方法,但在每种情况下,它都需要比实际需要的更多的代码和重复性编程。 要么,要么我完全错过了船哈哈! 在此先感谢您的帮助,我们将不胜感激并慷慨投票。

【问题讨论】:

  • 简而言之,因为B扩展了A,它继承了As方法,但A没有扩展B,所以它没有继承Bs方法;因此B::bark() 会起作用,A::speak() 不会——正如你所发现的。这只是静态方法的问题,因为它们属于类,而不是从该类实例化的任何对象。
  • 我知道“使用全局变量”是不受欢迎的,我同意,但这与它的使用方式有关。在这种情况下 - 将“辅助功能”专门分组到需要的内容,而不是“以防万一”一次加载所有内容。话虽如此,类中的属性和方法可以是私有的并最终声明,有效地“保护”了类及其方法。此外,系统可以编写一个“用户权限系统”,只允许每个用户使用此类类,并且应用程序可以是用户本身,并且都可以分别包含在命名空间中。
  • 静态方法有一席之地——不仅仅是“使用全局变量”。例如,假设您创建了一个 Email 类,并在其中有一些方法用于验证或其他。验证方法是实例方法是有意义的,因为它们适用于该对象中保存的电子邮件数据。现在,假设您要添加一个将应用于实例化的每个 电子邮件对象的黑名单。在这种情况下,使用静态方法是有意义的。使用静态方法,黑名单将在内存中保存一次,对于类,使用实例方法,每个电子邮件对象都会将黑名单保存在内存中。
  • “使用未知的方法,但在运行时加载,具体取决于应用程序的流程” – 我怀疑这实际上是个好主意……这听起来像是通向 God Object 的路径,它将在不同的上下文中做各种各样的事情——这通常是不可取的。但是你的 A/B 和吠叫/说话的“例子”太笼统了,很难判断你是否正在尝试实现一些真正有意义的东西,或者你是否已经完全偏离了正确的道路。
  • 听起来像一个糟糕的设计模式。很可能还有另一种方式,但如果没有更具体的例子,就很难告诉你是什么。

标签: php inheritance static


【解决方案1】:

我认为您可以利用 __call 魔术方法发挥一些创造力。 你可以这样做:

class A
{
    /**
     * @var array
     */
    protected $methods = [];

    public function __call($name, $arguments)
    {
        if (!empty($this->methods[$name]) && is_callable($this->methods[$name])) {
            return call_user_func_array($this->methods[$name], $arguments);
        }
    }

    public function addMethod($name, Closure $method)
    {
        $this->methods[$name] = $method;
    }
}

// get class instance (A have no methods except of addMethod)
$instance = new A();

// add methods on runtime
$instance->addMethod('sum', function($num1, $num2) {
    return $num1 + $num2;
});

$instance->addMethod('sub', function($num1, $num2) {
    return $num1 - $num2;
});

// use methods exactly the same way as implemented fixed on class
echo $instance->sum(2, 2);
echo $instance->sub(3, 2);

当然,您也可以将 __callStatic 用于静态方法。如果你想让它更复杂一点,也可以使用依赖注入的概念来添加对象而不是方法。然后通过注入的对象搜索被调用的方法,找到后调用。 我希望这至少能给你一个好主意。

【讨论】:

  • 好主意@David!通过addMethod添加的方法是否也可以在不调用new A()的子树中的其他方法或函数中使用?
  • 谢谢!是的,您可以使用 __callStatic 相同的方式添加和使用静态方法。
  • 这很棒,因为在这里使用您的模式,如果某些内容即将被覆盖,很容易引发用户异常,或者甚至可以重新路由对禁止的方法的调用某些用户组,并提供适当的响应。投票++!!
  • 没错,你可以做任何你想做的事。它是完全动态的并且非常简单。很高兴知道这个帮助:D
猜你喜欢
  • 2012-11-22
  • 1970-01-01
  • 2010-11-19
  • 2014-01-07
  • 1970-01-01
  • 2011-05-12
  • 1970-01-01
  • 1970-01-01
  • 2011-01-01
相关资源
最近更新 更多