Why does PHP 5.2+ disallow abstract static class methods?
在php 5.2中启用了严格警告之后,我看到了一个项目中的严格标准警告,这个项目最初是在没有严格警告的情况下编写的:
Strict Standards: Static function Program::getSelectSQL() should not be abstract in Program.class.inc
所讨论的函数属于抽象父类程序,并声明为抽象静态函数,因为它应在子类(如tvprogram)中实现。
我确实在这里找到了这个变化的参考文献:
Dropped abstract static class functions. Due to an oversight, PHP 5.0.x and 5.1.x allowed abstract static functions in classes. As of PHP 5.2.x, only interfaces can have them.
我的问题是:有人能清楚地解释为什么PHP中不应该有抽象的静态函数吗?
静态方法属于声明它们的类。扩展类时,可以创建同名的静态方法,但实际上并没有实现静态抽象方法。
用静态方法扩展任何类也一样。如果扩展该类并创建具有相同签名的静态方法,则实际上不会重写超类的静态方法。
编辑(2009年9月16日)对此进行更新。运行php 5.3时,我看到抽象静态返回,无论好坏。(有关详细信息,请参阅http://php.net/lsb)
更正(由Philfreo)php 5.3中仍然不允许使用
这是一个漫长而悲伤的故事。好的。
当PHP5.2首次引入此警告时,后期的静态绑定还没有使用该语言。如果您不熟悉最新的静态绑定,请注意,类似这样的代码并不像您期望的那样工作:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?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(); |
撇开严格的模式警告不谈,上面的代码不起作用。
鉴于此,PHP5.2中的抽象静态方法是无用的。使用抽象方法的整个要点是,您可以编写调用该方法的代码,而不知道它将调用什么实现,然后在不同的子类上提供不同的实现。但是,由于php 5.2没有提供一种干净的方法来编写一个父类的方法,该方法调用子类的静态方法,因此抽象静态方法的这种用法是不可能的。因此,在php 5.2中使用
但是后来又增加了php 5.3的功能,可以引用通过
添加了static关键字后,让
我想,你还是可以提出保留警告的理由。例如,您可能会争辩说,由于PHP允许您调用抽象类的静态方法,在我上面的示例中(甚至在用
所以基于这个论点,php开发人员用语言保存了警告,对吗?好的。
呃,不完全是。好的。
上面链接的php bug报告53081要求删除警告,因为添加了
Giorgio
i know, but:
Ok.
1
2
3
4
5
6
7
8
9
10
11
12
13 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();Rasmus
Right, that is exactly how it should work.
Ok.
Giorgio
but it is not allowed :(
Ok.
Rasmus
What's not allowed?
Ok.
1
2
3
4
5
6
7
8
9
10 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.Ok.
Rasmus声称他的示例中的代码"工作正常"是错误的;正如您所知,它抛出了一个严格的模式警告。我猜他在测试时没有打开严格的模式。不管怎样,一个困惑的拉斯穆斯错误地将请求关闭为"伪造"。好的。
这就是为什么警告仍然是用语言表达的。这可能不是一个完全令人满意的解释——你可能来这里是希望有一个合理的理由警告。不幸的是,在现实世界中,有时选择是从平凡的错误和错误的推理中产生的,而不是从理性的决策中产生的。这只是其中之一。好的。
幸运的是,作为php-rfc的一部分,可评估的nikita popov已经从php 7语言中删除了警告:重新分类e_-strict notices。最终,理智占了上风,一旦php 7发布,我们都可以愉快地使用
对于这个问题,有一个非常简单的解决方法,从设计的角度来看,这实际上是有意义的。正如乔纳森所写:
Same goes for extending any class with static methods. If you extend that class and create a static method of the same signature, you are not actually overriding the superclass's static method
所以,作为一项工作,你可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?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来创建具有类似功能的类。
我知道这是旧的,但是……
为什么不直接抛出该父类的静态方法的异常,这样如果不重写它,就会导致异常。
我认为抽象类/接口可以看作是程序员之间的契约。它更多地处理事物的外观/行为,而不是实现实际的功能。正如在php5.0和5.1.x中所看到的,这不是阻止PHP开发人员这样做的自然法则,而是促使其他语言中的OO设计模式一起使用的冲动。基本上,如果一个人已经熟悉其他语言,这些想法试图防止意外的行为。
我认为没有任何理由禁止静态抽象函数。没有理由禁止它们的最好的理由是,它们被允许在Java中使用。问题是:-技术上可行吗?-是的,因为存在于PHP 5.2中,它们存在于Java中。所以我能做到。我们应该这样做吗?-它们有意义吗?对。实现类的一部分并将类的另一部分留给用户是有意义的。它在非静态函数中是有意义的,为什么它不应该对静态函数有意义呢?静态函数的一种用法是类,其中不能有多个实例(单例)。例如,加密引擎。它不需要存在于多个实例中,并且有理由防止这种情况发生——例如,您只需要保护内存的一部分以防入侵者。因此,实现引擎的一部分并将加密算法留给用户是完全有意义的。这只是一个例子。如果你习惯使用静态函数,你会发现更多。
在php 5.4+中,使用特性:
1 2 3 4 5 | trait StaticExample { public static function instance () { return new self; } } |
在你们班上,把乞讨的话说出来:
1 | use StaticExample; |
研究PHP的"后期静态绑定"问题。如果您将静态方法放在抽象类上,您可能会很快遇到它,而不是很晚。严格的警告告诉您避免使用中断的语言功能是有意义的。