122

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

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

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

我确实在这里找到了有关此更改的参考:

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

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

4

8 回答 8

79

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

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

撇开严格模式警告不谈,上面的代码不起作用。self::bar()调用 infoo()显式引用 的方法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阅读更多关于selfvs的内容。static

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

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

所以基于这个论点,PHP 开发人员在语言中保留了警告,对吗?

呃,不完全是

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

乔治奥

我知道但是:

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(),但是 static::B() 很好。

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

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

Luckily, the estimable Nikita Popov has removed the warning from the language in PHP 7 as part of PHP RFC: Reclassify E_STRICT notices. Ultimately, sanity has prevailed, and once PHP 7 is released we can all happily use abstract static without receiving this silly warning.

于 2015-07-05T23:14:38.133 回答
76

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

使用静态方法扩展任何类也是如此。如果您扩展该类并创建具有相同签名的静态方法,您实际上并没有覆盖超类的静态方法

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


abstract staticPHP 5.3 中仍然不允许更正(由 philfreo 编写) , LSB是相关但不同的。

于 2009-06-16T00:14:31.167 回答
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 来创建一个具有类似功能的类。

于 2011-06-17T13:17:44.903 回答
12

我知道这是旧的但是......

为什么不直接在该父类的静态方法中抛出异常,这样如果您不覆盖它,就会导致异常。

于 2010-06-16T11:28:02.330 回答
4

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

于 2009-06-16T00:31:40.780 回答
2

我看不出有任何理由禁止静态抽象函数。没有理由禁止它们的最佳论据是,它们在 Java 中是允许的。问题是: - 技术上可行吗?- 是的,因为存在于 PHP 5.2 中并且它们存在于 Java 中。那么什么时候可以做到。我们应该这样做吗?- 它们有意义吗?是的。实现类的一部分并将类的另一部分留给用户是有意义的。它在非静态函数中有意义,为什么对静态函数没有意义?静态函数的一种用途是不能有多个实例(单例)的类。例如加密引擎。它不需要在多个实例中存在,并且有理由防止这种情况发生 - 例如,您只需保护内存的一部分免受入侵者的侵害。因此,实现引擎的一部分并将加密算法留给用户是非常有意义的。这只是一个例子。如果您习惯使用静态函数,您会发现更多。

于 2013-11-07T09:11:23.173 回答
0

在 php 5.4+ 中使用特征:

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

并在你的课堂上放在开头:

use StaticExample;
于 2014-09-01T11:22:06.223 回答
-1

查看 PHP 的“后期静态绑定”问题。如果您将静态方法放在抽象类上,您可能迟早会遇到它。严格的警告告诉您避免使用损坏的语言功能是有道理的。

于 2009-06-16T01:23:09.863 回答