【问题标题】:Detect static function called non-statically检测非静态调用的静态函数
【发布时间】:2016-10-31 01:43:37
【问题描述】:

如何检测非静态调用的静态函数?

例如,在这种情况下:

class Foo
{
    public static function bar()
    {
        // How can I tell here that bar() is called on an instance?

        //var_dump(debug_backtrace()[0]['type'] == '::');
        // at all times the above prints bool(true)
        return 1;
    }
}

// later in the code
$foo = new Foo();
$foo::bar(); // that's fine

// even later
$foo->bar(); // this should not happen, yet it's here and there

我想调试和消除上面最后一行的情况:有人在某个地方错误地调用了实例上的函数,期望它会返回与主题中的实例相关的东西;取而代之的是一个有点相关的常数。随后我需要知道这种情况何时发生。如果地狱破裂或抛出异常对我来说也很好。

到目前为止,我发现静态函数的非静态调用是 internally translated into static calls,因此 debug_backtrace()[0]['type'] 什么也没告诉我们(在这两种情况下都是 ::)。

【问题讨论】:

  • 静态调用非静态函数是错误的。非静态调用静态函数是不是。您的代码是后者的示例,而不是前者。
  • @Sherif 我更新了这个问题,更清楚地表明这不是错误
  • 我想我当时想知道的是,如果它不是错误的话,为什么你会关心检测这个呢?让我失望的是你使用了“debug these cases”语句,这让我相信你的印象是这会导致错误的行为。它不能。 PHP 在编译时已经知道该方法是静态声明的。所以它并不关心您是否尝试在运行时从对象实例调用方法。它仍然知道使用静态上下文。
  • @Sherif 它肯定会导致我正在尝试修复的遗留代码中出现错误行为;不知道问题出在哪里,我应该如何解决?
  • 您看到的这种错误行为是什么?很可能它与非静态调用静态方法无关,这就是为什么我想知道你为什么会问这样一个奇怪的问题。也许尝试呈现产生问题的实际代码并解释所需行为与实际问题的实际行为?因为现在您在这个问题中提供的代码没有显示任何问题。

标签: php


【解决方案1】:

解决方案有两个:

  1. 第一个需要禁用E_STRICT报告:

    error_reporting(error_reporting() ^ E_STRICT);
    
  2. 下一个应该从函数的声明中删除 static 关键字。

    public /* static */ function bar()
    

现在可以在debug_backtrace函数的帮助下查看函数是静态调用还是动态调用:

class Foo
{
    public function bar()
    {
        $calledStatically = debug_backtrace()[0]['type'] == '::';

        if (!$calledStatically) {
            throw new Exception("Should not happen");
        }

        // ...
    }
}

来自文档:

type(字符串):当前调用类型。如果是方法调用,则返回“->”。如果一个静态 方法调用,返回“::”。如果一个函数调用,什么都不是 返回。

快速演示。

Foo::bar(); // normal result returned

$foo = new Foo();
$foo->bar(); // throws exception

【讨论】:

  • 正如我在问题中指出的那样,debug_backtrace 并没有像您认为的那样提供帮助。
  • $utils = new Utils(); $utils::whoDat(); $utils->whoDat(); 这应该如何工作?
  • 好的,上次更新...不确定删除“静态”是否是一个有效选项。但这似乎行得通。
  • 是的,如果你禁用E_STRICT reporting for a while,它就可以工作
  • 不,去吧
【解决方案2】:

如果您不想永远关闭E_STRICT,有办法。

如果类 Foo 是幸运的或天意,只在几个地方实例化,如下所示:

public function getFoo()
{
    return new Foo();
}

那么如果我们对它进行子类化并重新定义bar():

class Foo2 extends Foo
{
    public static function bar()
    {
         throw new Exception("You're calling Foo::bar() on an instance");
    }
}

那么如果我们将 Foo 替换为 Foo2 在它被实例化的地方......

public function getFoo()
{
    //return new Foo();
    return new Foo2();
}

调用上述新的静态方法会抛出异常:

$q = Foo::bar(); // no error just as before

$foo = $other->getFoo();
$foo->bar(); // throws exception

如果有人在这种Foo 上致电$foo->bar();,他会很快被告知他犯了一个错误。

【讨论】:

    猜你喜欢
    • 2018-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-14
    • 2017-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多