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

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

严格标准 : 静态功能 Program: : getSelectSQL () Program.class.inc 中的 should not be abstract

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

我确实找到了这个更改的参考资料 给你:

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

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

66754 次浏览

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

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

2009年9月16日
最新消息。运行 PHP 5.3,我看到抽象静态回来了,无论好坏。(更多信息见 http://php.net/lsb)

更正 (by philfreo)
在 PHP 5.3中仍然不允许使用 abstract staticLSB是相关但不同的。

我认为抽象类/接口可以看作是程序员之间的契约。它更多地处理事情应该看起来/行为如何,而不是实现实际的功能。正如在 php5.0和5.1. x 中看到的那样,阻止 php 开发人员这样做并不是一条自然法则,而是迫切希望使用其他语言的其他面向对象设计模式。基本上,如果一个人已经熟悉其他语言,这些想法试图防止意外的行为。

研究 PHP 的“后期静态绑定”问题。如果您将静态方法放在抽象类上,那么可能很快就会遇到这种情况。严格的警告告诉您避免使用破碎的语言特性,这是有道理的。

我知道这很旧了,但是..。

Why not just throw an exception the that parent class's static method, that way if you don't override it the exception is caused.

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

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

因此,作为一项工作,你可以这样做:

<?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 静态方法和一个公共 getSome 数据方法。如果没有子类 MyFoo,仍然可以实现 iMyFoo 来创建具有类似功能的类。

我看不出有任何理由禁止静态抽象函数。认为没有理由禁止它们的最佳论据是,它们在 Java 中是允许的。 问题是: - 技术上可行吗?-是的,因为 PHP 5.2和 Java 中都有。 那么,我们能做到这一点。我们应该这样做吗? 有意义吗?是的。实现类的一部分并将类的另一部分留给用户是有意义的。它在非静态函数中是有意义的,为什么它对静态函数没有意义呢?静态函数的一个用法是不能有多个实例(单例)的类。例如,加密引擎。它不需要存在于多个实例中,并且有理由防止这种情况发生——例如,您必须仅保护内存的一部分免受入侵者的侵害。因此,实现引擎的一部分并将加密算法留给用户是非常有意义的。 This is only one example. If you are accustomed to use static functions you'll find lots more.

在 php 5.4 + 中使用 trait:

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

在你的课堂上把开头写上:

use StaticExample;

说来话长。

当 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()调用明确地引用 ParentClassbar()方法,即使当 foo()被作为 ChildClass的方法调用时也是如此。如果您尝试关闭严格模式运行此代码,您将看到“ PHP 致命错误: 不能调用抽象方法 ParentClass: : bar ()”。

鉴于此,PHP 5.2中的抽象静态方法毫无用处。使用抽象方法的 entire point是,您可以编写调用该方法的代码,而不必知道它将调用什么实现——然后在不同的子类上提供不同的实现。但是,由于 PHP 5.2没有提供清晰的方法来编写父类的方法来调用调用它的子类的静态方法,因此不可能使用抽象静态方法。因此,在 PHP 5.2中使用 abstract static都是糟糕的代码,可能是由于对 self关键字的工作原理的误解。对此提出警告是完全合理的。

但是 PHP 5.3增加了引用通过 static关键字调用方法的类的能力(不像 self关键字,它总是引用方法是 定义的类)。如果在我上面的示例中将 self::bar()改为 static::bar(),那么它在 PHP 5.3及以上版本中工作得很好。你可以在 新的自我与新的静态阅读更多关于 selfstatic的内容。

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

我想,你还是可以提出保留警告的理由。例如,你可能会争辩说,因为 PHP 允许你调用抽象类的静态方法,在我上面的例子中(即使通过用 static替换 self来修复它) ,你正在公开一个公共方法 ParentClass::foo(),它就是 支离破碎,而你并不真的想公开它。使用一个非静态类——也就是说,使所有的方法实例方法和使 ParentClass的子类都是单例或类似的东西——将解决这个问题,因为 ParentClass是抽象的,不能被实例化,所以它的实例方法不能被调用。我认为这个论点是站不住脚的(因为我认为暴露 ParentClass::foo()没什么大不了的,使用单例代替静态类通常是不必要的冗长和丑陋的) ,但是你可能有理由不同意——这是一个有点主观的调用。

So based upon this argument, the PHP devs kept the warning in the language, right?

不完全是.

上面链接的 PHP bug 报告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 () 没关系。

The claim by Rasmus that the code in his example "works fine" is false; as you know, it throws a strict mode warning. I guess he was testing without strict mode turned on. Regardless, a confused Rasmus left the request erroneously closed as "bogus".

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

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