【问题标题】:__callStatic is not called?__callStatic 没有被调用?
【发布时间】:2012-11-07 00:02:31
【问题描述】:

我知道以下内容可能会在其他地方产生问题并且可能是糟糕的设计,但我仍然想知道为什么会失败(为了我自己的启发):

class Test {
    // singleton
    private function __construct(){}
    private static $i;
    public static function instance(){
        if(!self::$i){
            self::$i = new Test();
        }
        return self::$i;
    }
    // pass static requests to the instance
    public static function __callStatic($method, $parameters){
        return call_user_func_array(array(self::instance(), $method), $parameters);
    }
    private $someVar = 1;
    public function getSomeVar(){
        return $this->someVar;
    }
}

print Test::getSomeVar();

错误是Using $this when not in object context

显然 $this 在静态方法中是不可用的,但静态方法通过 call_user_func_array 将其交给实例方法调用,这应该使 $this 成为实例...

/编辑

我知道 $this 在静态上下文中不可用。但是,这是可行的:

print call_user_func_array(array(Test::instance(), 'getSomeVar'), array());

这正是 __callStatic 重载中发生的事情,所以有些不对劲......

/编辑 2

scope 肯定会被奇怪地处理。如果您将单例实例拉到任何其他类,它会按预期工作:

class Test {
    // singleton
    private function __construct(){}
    private static $i;
    public static function instance(){
        if(!self::$i){
            self::$i = new Blah();
        }
        return self::$i;
    }
    // pass static requests to the instance
    public static function __callStatic($method, $parameters){
        return call_user_func_array(array(static::instance(), $method), $parameters);
    }
}

class Blah {
    private $someVar = 1;
    public function getSomeVar(){
        return $this->someVar;
    }
}

print Test::getSomeVar();

【问题讨论】:

  • 引用 PHP 手册:__callStatic() is triggered when invoking inaccessible methods in a static context. - 您的方法是公开的,将其设为私有/受保护的,它将按您的预期工作;)参考:php.net/manual/en/…
  • (当然,目标 static 方法也应该定义为 static )

标签: php class static


【解决方案1】:

您不能通过 PHP 中的__callStatic 调用staticfy您的对象方法。它只会在该方法目前不存在(包括从调用上下文中不可见)时被调用。在您的情况下,Test::getSomeVar() 已经定义。

由于向后兼容,PHP 不检查是否只存在静态方法,而是检查是否存在一般方法。

在您的情况下,您正在静态调用非静态方法,因此未定义 $this,因为尚未调用 __callStatic。如果您将警告和通知启用到最高级别(推荐用于开发),PHP 会警告您。

因此正确的用法是:

echo Test::instance()->getSomeVar();

与任何其他单例实现一样。

所以你只是错误地使用了__callStatic,它只适用于尚未定义的方法。为工作选择另一个工具/设计,例如您在 Blah 类中使用的聚合示例。

进一步说明:

无论如何,您通常都应该避免在 PHP 中使用任何静态上下文,尤其是当您在这里也利用了许多神奇的功能时,这是另一种味道。看起来您在完成代码之前有一堆设计问题。所有这些只会增加您遇到难以调试和维护代码的可能性。但这只是为了让你不要说在某些时候你没有被警告过。

如果您想了解更多信息,请查看Who needs singletons?

【讨论】:

  • @hakre 感谢您的详细回复 - 为了清楚起见,我会接受。这不是我个人正在做的事情,而是对一些新的流行框架和微框架进行调查的结果。事实证明,例如 laravel,它是 PHP 框架领域备受推崇的(但相当新的)进入者,它使用了我上次编辑中描述的模式(Blah 类 - 他们称之为“有效负载”)。您的帖子解释了为什么它们将静态实例分开。谢谢
  • 如果他们使用静态调用进行装饰,Lavarel 是一个创建全局变量的框架。通常是多余的,PHP 提供了开箱即用的全局变量。可能他们对部分代码并不真正想要这个,所以他们通过 decoration 添加静态调用,这是一种更常见的模式:en.wikipedia.org/wiki/Decorator_pattern - 但是静态装饰器会破坏听起来不正确的灵活性对于一个框架,也是 - 另见i.imgur.com/RJEsz.png
  • @hakre 很有趣。他们称其为“外观”模式,但我确实深入研究了内部结构,他们使用的方法与我的“Blah”示例中描述的方法相同。根据thenerdary.net/post/30859565484/laravel-4,看起来下一个版本 (4) 将实现您描述的装饰器模式,仍然使用静态成员通过公共应用程序变量访问单个入口点。
  • en.wikipedia.org/wiki/Facade_pattern - 外观模式 - 最多只有一个 - 方法。您的示例中提供的静态方法访问器降低了从对象中受益的灵活性,因此是一种单向方法,我不会过多地使用常见的模式名称,因为在基于类的编程和全局(静态)变量中,这一切都会发生进入错误的方向。请注意,在与其他开发人员讨论时,您可能会对这些术语感到困惑。
  • @hakre,好消息。我正在阅读 IoC 和 DI 并认为我明白了这一点。
【解决方案2】:

它不是那样工作的。在static 调用中,您没有实例、没有对象,也没有$this。调用 static 仅适用于 $this 不可用的静态方法。

文档说:

__callStatic() 在在静态上下文中调用不可访问的方法时触发。

我知道这不是您希望的答案。正如您所预料的那样:您不需要在 PHP 中重载,只需创建您的应用程序并将其排除在外。

【讨论】:

  • 问题是对“在静态上下文中”的含义有误解。 “上下文”是它被调用的“位置”。如果您从 Widget 对象中调用 Widget::foo()。它在对象上下文中被调用。 “::”并不意味着“静态调用”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-15
  • 1970-01-01
  • 1970-01-01
  • 2018-11-25
  • 2019-03-31
  • 2012-12-16
  • 2015-03-28
相关资源
最近更新 更多