为什么 PHP 5.2+ 不允许抽象静态类方法?

2022-08-30 07:11:40

在 PHP 5.2 中启用严格警告后,我看到一个最初没有严格警告的项目的大量严格标准警告:

严格标准静态函数 Program::getSelectSQL() 在 Program.class.inc 中不应该是抽象

所讨论的函数属于抽象父类 Program,并且被声明为抽象静态,因为它应该在其子类(如 TVProgram)中实现。

我确实在这里找到了对此更改的引用:

删除了抽象静态类函数。由于疏忽,PHP 5.0.x 和 5.1.x 允许在类中使用抽象静态函数。从 PHP 5.2.x 开始,只有接口可以拥有它们。

我的问题是:有人可以清楚地解释为什么PHP中不应该有抽象的静态函数吗?


答案 1

这是一个漫长而悲伤的故事。

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

撇开严格模式警告不谈,上面的代码不起作用。中的调用显式引用 的方法,即使作为 的方法调用 也是如此。如果您尝试在关闭严格模式的情况下运行此代码,您将看到“PHP 致命错误:无法调用抽象方法 ParentClass::bar()”。self::bar()foo()bar()ParentClassfoo()ChildClass

鉴于此,PHP 5.2 中的抽象静态方法是无用的。使用抽象方法的全部意义在于,您可以编写调用该方法的代码,而不知道它将调用什么实现 - 然后在不同的子类上提供不同的实现。但是,由于 PHP 5.2 没有提供编写父类方法的干净方法,该方法调用调用其所基于的子类的静态方法,因此这种抽象静态方法的使用是不可能的。因此,PHP 5.2 中的任何用法都是糟糕的代码,可能是受到对关键字工作原理的误解的启发。对此提出警告是完全合理的。abstract staticself

但后来PHP 5.3增加了引用通过关键字调用方法的类的能力(与关键字不同,关键字总是引用定义方法的类)。如果你在上面的例子中改成,它在PHP 5.3及更高版本中工作正常。你可以阅读更多关于 vs 在 新的自我 vs. 新的静态.staticselfself::bar()static::bar()selfstatic

添加 static 关键字后,抛出警告的明确论据就消失了。后期静态绑定的主要目的是允许在父类中定义的方法调用将在子类中定义的静态方法;考虑到后期静态绑定的存在,允许抽象的静态方法似乎是合理和一致的。abstract static

我猜,你仍然可以提出一个保持警告的理由。例如,你可以争辩说,由于PHP允许你调用抽象类的静态方法,在我上面的例子中(即使在通过替换为修复它之后),你也暴露了一个公共方法,它坏了,你并不是真的想公开。使用非静态类 - 即,使所有方法的实例方法并使所有方法的子级都是单例或其他东西 - 将解决此问题,因为抽象的无法实例化,因此无法调用其实例方法。我认为这个论点很弱(因为我认为公开并不是什么大问题,使用单例而不是静态类通常是不必要的冗长和丑陋),但你可能会合理地不同意 - 这是一个有点主观的呼吁。selfstaticParentClass::foo()ParentClassParentClassParentClass::foo()

因此,基于这个论点,PHP开发人员将警告保留在语言中,对吧?

呃,不完全是

上面链接的PHP错误报告53081要求删除警告,因为添加构造使抽象静态方法变得合理和有用。Rasmus Lerdorf(PHP的创建者)首先将请求标记为虚假,并经过一长串不良推理来试图证明警告的合理性。然后,最后,进行这种交换:static::foo()

乔治

我知道,但是:

abstract class cA
{
      //static function A(){self::B();} error, undefined method
      static function A(){static::B();} // good
      abstract static function B();
}

class cB extends cA
{
    static function B(){echo "ok";}
}

cB::A();

拉斯穆斯

是的,这正是它应该如何工作。

乔治

但不允许:(

拉斯穆斯

什么是不允许的?

abstract class cA {
      static function A(){static::B();}
      abstract static function B();
}

class cB extends cA {
    static function B(){echo "ok";}
}

cB::A();

这工作正常。你显然不能调用self::B(),但静态::B()是可以的。

Rasmus声称他的例子中的代码“工作正常”是错误的;如您所知,它会抛出严格的模式警告。我猜他是在没有打开严格模式的情况下进行测试的。无论如何,困惑的Rasmus错误地关闭了请求,因为“虚假”。

这就是为什么警告仍然在语言中。这可能不是一个完全令人满意的解释 - 你可能来到这里,希望有一个合理的警告理由。不幸的是,在现实世界中,有时选择是源于平凡的错误和糟糕的推理,而不是理性的决策。这只是其中一次。

幸运的是,可估量的Nikita Popov已经从PHP 7的语言中删除了警告,作为PHP RFC的一部分:重新分类E_STRICT通知。最终,理智占了上风,一旦PHP 7发布,我们都可以愉快地使用,而不会收到这个愚蠢的警告。abstract static


答案 2

静态方法属于声明它们的类。扩展类时,可以创建同名的静态方法,但实际上并不是在实现静态抽象方法。

使用静态方法扩展任何类也是如此。如果扩展该类并创建具有相同签名的静态方法,则实际上不会重写超类的静态方法

编辑(2009年9月16日)
更新。运行PHP 5.3,我看到抽象静态又回来了,不管是好是坏。(有关详细信息,请参阅 http://php.net/lsb

更正(由 philfreo)
在 PHP 5.3 中仍然不允许,LSB 是相关的,但不同。abstract static


推荐