这是一个悲伤而冗长的故事。
当PHP 5.2首次引入此警告时,静态绑定延迟还未在语言中出现。如果您对静态绑定延迟不熟悉,请注意这样的代码并不能按照您的预期工作:
<?php
abstract class ParentClass {
static function foo() {
echo "I'm gonna do bar()";
self::bar();
}
abstract static function bar();
}
class ChildClass extends ParentClass {
static function bar() {
echo "Hello, World!";
}
}
ChildClass::foo();
除了严格模式警告之外,上面的代码不起作用。在foo()中的self::bar()调用明确地引用了ParentClass的bar()方法,即使foo()作为ChildClass的方法被调用也是如此。如果你关闭严格模式并尝试运行这段代码,你会看到"PHP致命错误:无法调用抽象方法ParentClass::bar()"。
鉴于此,在PHP 5.2中抽象静态方法是无用的。使用抽象方法的整个重点是可以编写调用该方法的代码,而不知道它将调用哪个实现 - 然后在不同的子类上提供不同的实现。但由于PHP 5.2没有清洁的方式编写父类的方法来调用所调用的子类的静态方法,因此无法使用抽象静态方法。因此,在PHP 5.2中使用abstract static是糟糕的代码,可能是受到了对self关键字工作原理的误解的启发。对此发出警告是完全合理的。
但是 PHP 5.3 推出了一个新功能,可以通过
static
关键字引用调用方法的类(与
self
关键字不同,后者总是指在该方法中定义的类)。如果您在上面的示例中将
self::bar()
更改为
static::bar()
,则在 PHP 5.3 及以上版本中可以正常工作。您可以在
New self vs. new static 中了解更多关于
self
vs
static
的内容。
有了添加的 static 关键字,
abstract static
抛出警告的明显理由消失了。延迟静态绑定的主要目的是允许在父类中定义的方法调用在子类中定义的静态方法;鉴于延迟静态绑定的存在,允许抽象静态方法似乎是合理和一致的选择。
您仍然可以为保留警告辩护。例如,您可以认为由于PHP允许您调用抽象类的静态方法,在我的示例中(即使通过替换
self
为
static
进行了修复),您正在公开一个
ParentClass :: foo()
的公共方法,它是
错误的,而您不真正希望公开它。使用非静态类 - 即将所有方法作为实例方法,并使
ParentClass
的子代都成为单例或其他内容 - 可以解决此问题,因为抽象类
ParentClass
无法被实例化,因此无法调用其实例方法。我认为这个论点很弱(因为我认为暴露
ParentClass :: foo()
并不是一个大问题,而使用单例代替静态类通常是不必要的冗长和丑陋的),但您可能会有不同的看法 - 这是一个主观性较强的判断。
因此,基于这个论点,PHP开发人员在语言中保留了警告,对吗?
嗯,不完全是这样。
与PHP相关的bug报告53081建议删除警告,因为static::foo()
构造的添加使得抽象静态方法变得合理和有用。PHP的创建者Rasmus Lerdorf将该请求标记为虚假,并通过一系列错误的推理试图证明该警告的合理性。最后,发生了如下交换:
Giorgio
i know, but:
abstract class cA
{
static function A(){static::B();}
abstract static function B();
}
class cB extends cA
{
static function B(){echo "ok";}
}
cB::A();
Rasmus
Right, that is exactly how it should work.
Giorgio
but it is not allowed :(
Rasmus
What's not allowed?
abstract class cA {
static function A(){static::B();}
abstract static function B();
}
class cB extends cA {
static function B(){echo "ok";}
}
cB::A();
This works fine. You obviously can't call self::B(), but static::B()
is fine.
Rasmus声称他的例子中代码“运行良好”是错误的;如您所知,它会抛出严格模式警告。我猜他在没有开启严格模式的情况下进行测试。不管怎样,困惑的Rasmus错误地将请求关闭为“虚假”。因此,这就是为什么警告仍然存在于语言中的原因。这可能不是完全令人满意的解释 - 您可能来到这里希望有一个合理的警告正当化的解释。不幸的是,在现实世界中,有时选择是源于平凡的错误和错误的推理而不是理性的决策制定。这只是其中之一。幸运的是,可敬的Nikita Popov已经在PHP 7中将警告从语言中移除,作为
PHP RFC:重新分类E_STRICT通知的一部分。最终,理智获胜,一旦发布了PHP 7,我们就可以愉快地使用
abstract static
而不会收到这个愚蠢的警告。