【问题标题】:Why does PHP allow abstract static functions为什么 PHP 允许抽象静态函数
【发布时间】:2017-05-27 10:26:44
【问题描述】:

考虑以下代码:

abstract class ExampleClass
{
    public static function regularStaticFunction()
    {
        return static::abstractStaticFunction();
    }

    abstract protected static function abstractStaticFunction();
}

ExampleClass::regularStaticFunction();

PhpStorm IDE 对 abstractStaticFunction 的声明发出警告,内容如下:

PHP 严格标准:静态函数“abstractStaticFunction”不应该是抽象的。

静态函数不应该是抽象的。

但是,PHP 在解析此类时会继续执行程序并输出以下内容:

PHP 严格标准:静态函数 ExampleClass::abstractStaticFunction() 在第 7 行的 php shell 代码中不应是抽象的

在我看来,因为 PHP 允许在抽象类上调用静态函数,所以在抽象类上定义抽象静态函数应该是不可能的。

为什么解释器在 PHP 中允许抽象静态函数,但它们是无意义的?

【问题讨论】:

  • 我已阅读该问题,但没有看到我的问题的答案。为什么 PHP 解释器能够解析抽象的静态函数,当它们是无意义的。
  • 呸,这又一次投票关闭它作为stackoverflow.com/questions/999066/… 的副本。我不明白为什么;对我来说似乎很明显,询问为什么不允许抽象静态函数的问题与询问为什么允许它们的问题不重复。两者之间的区别并不微妙。他们在问相反的事情!

标签: php abstract-class interpreter abstract-function


【解决方案1】:

这是来自this answerMark Amery的很好的解释:

PHP bug report 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 声称他的示例中的代码“工作正常”是 错误的;如您所知,它会引发严格模式警告。我猜他是 没有开启严格模式的测试。无论如何,困惑的拉斯穆斯 将请求错误地关闭为“虚假”。

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

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

【讨论】:

  • -1;这回答了几乎是 相反 的问题 - 为什么abstract static 函数是不允许 允许的(从广义上讲,它们会触发警告)。这里的 OP 而是询问为什么他们被允许,这个答案并没有真正解决。
  • 我的 PHP 7.4 版本不生成 E_STRICT
【解决方案2】:

抽象静态函数并非没有意义!事实上,在我看来,它们使一些设计比我在没有它们的语言(如 Java 或 C#)中不得不求助的更简单、更清晰。

让我们举个例子。假设我正在编写某种普通的企业业务应用程序,它需要在两个 API 之间同步一些业务对象。这些 API 具有可以相互映射的对象模型,但使用不同的名称和不同的序列化格式。

在 PHP 中,借助抽象静态方法,我可以为这些业务对象类型定义一个抽象基类,如下所示...

abstract class ApiObject {
    /** The REST resource URL for this object type in the Foo API. */
    abstract static function fooApiResourceUrl();

    /** The REST resource URL for this object type in the Bar API. */
    abstract static function barApiResourceUrl();

    /** Given an XML response from the Foo API representing an object of this
        type, construct an instance. */
    abstract static function fromFooXml($xml);

    /** Given a JSON response from the Bar API representing an object of this
        type, construct an instance. */
    abstract static function fromBarJson($json);

    /** Serialize this object in the XML format that the Foo API understands */
    abstract function toFooXml();

    /** Serialize this object as JSON that the Bar API understands */
    abstract function toBarJson();
}

...然后我创建的每个具体子类都将保证提供从两个 API 中的任何一个获取它并反序列化它所需的所有信息,或者将它序列化并将它发送到任一 API。然后,以后,我可以写一些这样的代码:

// Ensure that all instances of these types that exist in the Foo API also
// exist in the Bar API:
$classesToSync = ['Widget', 'Frobnicator', 'Lead', 'Invoice'];
foreach ($classesToSync as $apiObjectClass) {
    $fooObjXmls = httpGetRequest($apiObjectClass::fooApiResourceUrl());
    foreach ($fooObjXmls as $fooObjXml) {
        $fooObj = $apiObjectClass::fromFooXml($fooObjXml);
        $json = $fooObj->toBarJson();
        httpPutRequest($apiObjectClass::barApiResourceUrl(), $json);
    }
}

我是否严格需要抽象静态方法来编写上面的程序?不;我还可以使用其他模式,例如让每个模型类与相应的工厂类配对,该工厂类负责从其 JSON 或 XML 表示中实例化它。但是这样的设计比我上面展示的更复杂。

那么,为什么它们被允许的答案很简单,就是它们有用,因为它们启用了一些没有它们就不可能实现的漂亮、简单的模式。当然,也存在反对它们的参数,其中一个在问题中给出 - 鉴于抽象类上的静态方法是可调用的,在类上公开抽象静态方法是很丑的。我不觉得这样的考虑特别有说服力,但即使你这样做了,它们与抽象静态方法提供的实用程序之间仍然存在权衡,PHP 维护人员可能已经权衡了它们并选择了让抽象静态方法的一面存在。

【讨论】:

    猜你喜欢
    • 2013-09-15
    • 1970-01-01
    • 2018-09-10
    • 2019-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 2020-10-20
    相关资源
    最近更新 更多