为什么PHP允许抽象静态函数

时间:2017-01-12 10:42:59

标签: php abstract-class interpreter abstract-function

请考虑以下代码:

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中允许抽象静态函数,当它们没有意义时?

2 个答案:

答案 0 :(得分:19)

这是来自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   将请求错误地关闭为" bogus"。

     

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

     幸运的是,可贵的尼基塔波波夫已经取消了警告   PHP 7中的语言是PHP RFC: Reclassify E_STRICT notices的一部分。最终,   理智已经占了上风,一旦PHP 7发布,我们都可以愉快   使用abstract static时不会收到这个愚蠢的警告。

答案 1 :(得分:1)

抽象静态函数并非荒谬!实际上,就我的感觉而言,它们使某些设计比我在没有它们的语言(例如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维护者大概已经权衡了它们,选择了让抽象静态方法使用存在。