为什么PHP 5.2+禁止抽象静态类方法?

时间:2009-06-16 00:07:04

标签: php oop static abstract

在PHP 5.2中启用严格警告后,我看到一个项目的严格标准警告,这些警告最初是在没有严格警告的情况下编写的:

  

严格标准静态功能 Program.class.inc中的程序:: getSelectSQL()不应该是抽象的

有问题的函数属于抽象父类Program,并被声明为abstract static,因为它应该在其子类中实现,例如TVProgram。

我确实找到了对此更改的引用here

  

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

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

8 个答案:

答案 0 :(得分:76)

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

使用静态方法扩展任何类都是一样的。如果扩展该类并创建相同签名的静态方法,则实际上并未覆盖超类的静态方法

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

更正(由philfreo提供)
在PHP 5.3中仍然不允许abstract staticLSB是相关但不同的。

答案 1 :(得分:72)

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

当PHP 5.2首次引入此警告时,late static bindings尚未使用该语言。如果你不熟悉后期静态绑定,请注意这样的代码不会像你期望的那样工作:

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

不考虑严格模式警告,上面的代码不起作用。 self::bar()中的foo()来电明确引用了bar() ParentClass方法,即使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了解有关selfstatic的更多信息。

添加了static关键字后,abstract static抛出警告的clear参数消失了。后期静态绑定的主要目的是允许父类中定义的方法调用将在子类中定义的静态方法;鉴于存在后期静态绑定,允许抽象静态方法似乎是合理和一致的。

我猜,你仍然可以提出保持警告的理由。例如,您可以争辩说,因为PHP允许您调用抽象类的静态方法,所以在上面的示例中(即使在用self替换static来修复它之后),您也暴露了一个公共方法{{ 1}} 已损坏并且你真的不想公开。使用非静态类 - 即,使所有方法实例方法并使ParentClass::foo()的子项都成为单例或其他东西 - 将解决这个问题,因为ParentClass是抽象的,不能被实例化,因此无法调用其实例方法。我认为这个论点很弱(因为我觉得暴露ParentClass并不是什么大问题而且使用单例代替静态类通常是不必要的冗长和丑陋),但你可能会合理地反对 - 这是一种有点主观的看法。 / p>

基于这个论点,PHP开发人员用语言保留警告,对吗?

呃,not exactly

上面链接的PHP错误报告53081要求删除警告,因为ParentClass::foo()构造的添加使得抽象静态方法合理且有用。 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();
     

这很好用。你显然不能调用self :: B(),但是静态:: B()   很好。

Rasmus声称他的例子中的代码“工作正常”是错误的;如你所知,它会抛出严格模式警告。我猜他没有打开严格模式就开始测试了。无论如何,一个混乱的Rasmus将请求错误地关闭为“虚假”。

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

幸运的是,可贵的Nikita Popov已经删除了PHP 7中语言的警告,作为PHP RFC: Reclassify E_STRICT notices的一部分。最终,理智得到了普及,一旦PHP 7发布,我们都可以愉快地使用abstract class cA { static function A(){static::B();} abstract static function B(); } class cB extends cA { static function B(){echo "ok";} } cB::A(); 而不会收到这个愚蠢的警告。

答案 2 :(得分:70)

对于这个问题,有一个非常简单的方法,从设计的角度来看,这实际上是有意义的。正如乔纳森所写:

  

使用静态方法扩展任何类都是一样的。如果扩展该类并创建相同签名的静态方法,则实际上并未覆盖超类的静态方法

所以,作为一项解决方案你可以做到这一点:

<?php
abstract class MyFoo implements iMyFoo {

    public static final function factory($type, $someData) {
        // don't forget checking and do whatever else you would
        // like to do inside a factory method
        $class = get_called_class()."_".$type;
        $inst = $class::getInstance($someData);
        return $inst;
    }
}


interface iMyFoo {
    static function factory($type, $someData);
    static function getInstance();
    function getSomeData();
}
?>

现在你强制要求任何继承MyFoo的类实现一个getInstance静态方法和一个公共getSomeData方法。如果你不是MyFoo的子类,你仍然可以实现iMyFoo来创建一个具有类似功能的类。

答案 3 :(得分:12)

我知道这已经老了但是......

为什么不抛出那个父类的静态方法的异常,如果你不覆盖它就会导致异常。

答案 4 :(得分:4)

我认为抽象类/接口可以被视为程序员之间的契约。它更多地涉及事物的外观/行为方式,而不是实现实际的功能。正如在php5.0和5.1.x中所看到的那样,这不是一个阻止php开发人员这样做的自然法则,而是在其他语言中与其他OO设计模式一起使用的冲动。基本上这些想法试图防止意外行为,如果一个人已经熟悉其他语言。

答案 5 :(得分:2)

我认为没有理由禁止静态抽象函数。没有理由禁止它们的最好的论点是,Java中允许它们。 问题是: - 技术上可行吗? - 是的,因为它存在于PHP 5.2中并且它们存在于Java中。 所以可以做到。我们应该这样做吗? - 他们有意义吗?是。实现类的一部分并将类的另一部分留给用户是有意义的。它在非静态函数中有意义,为什么静态函数不合理呢?静态函数的一个用途是不能有多个实例(单例)的类。例如加密引擎。它不需要存在于多个实例中,并且有理由阻止这种情况 - 例如,您必须仅保护内存的一部分以防止入侵者。因此,实现引擎的一部分并将加密算法留给用户是完全合理的。 这只是一个例子。如果你习惯使用静态函数,你会发现更多。

答案 6 :(得分:0)

在php 5.4+中使用trait:

trait StaticExample {
    public static function instance () {
    return new self;
    }
}

并在你的课堂上放置了开始:

use StaticExample;

答案 7 :(得分:-1)

了解PHP的'Late Static Binding'问题。如果你将静态方法放在抽象类上,你可能会很快就会遇到它。有意义的是,严格的警告告诉您要避免使用破坏的语言功能。