代码之家  ›  专栏  ›  技术社区  ›  Artem Russakovskii

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

  •  119
  • Artem Russakovskii  · 技术社区  · 15 年前

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

    严格的标准 : 静态函数 程序::GetSelectSql() 不应该是抽象的 在program.class.inc中

    所讨论的函数属于抽象父类程序,并声明为抽象静态函数,因为它应在子类(如tvprogram)中实现。

    我确实找到了这个变化的参考资料 here :

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

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

    8 回复  |  直到 15 年前
        1
  •  74
  •   Jonathan Fingland    13 年前

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

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

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

    修正 (由费弗罗)
    abstract static 在php 5.3中仍然不允许, LSB 相关但不同。

        2
  •  71
  •   Community CDub    8 年前

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

    当php 5.2第一次引入这个警告时, late static bindings 还没有用语言。如果您不熟悉最新的静态绑定,请注意,这样的代码可能无法按预期工作:

    <?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() 拜访 foo() 明确提及 bar() 方法 ParentClass ,甚至当 英尺() 作为的方法调用 ChildClass . 如果尝试在关闭严格模式的情况下运行此代码,您将看到“ PHP致命错误:无法调用抽象方法ParentClass::Bar()。 “。

    鉴于此,PHP5.2中的抽象静态方法是无用的。这个 整点 使用抽象方法的好处在于,您可以编写调用该方法的代码,而不知道它将调用什么实现,然后在不同的子类上提供不同的实现。但是,由于php 5.2没有提供一种干净的方法来编写调用子类的静态方法的父类的方法,因此抽象静态方法的这种用法是不可能的。因此任何使用 abstract static 在php 5.2中是错误的代码,可能是由于对 self 关键字工作。对此提出警告是完全合理的。

    但后来又增加了php 5.3的引用类的能力,通过 static 关键字(与 自己 关键字,它始终引用方法所在的类 定义 )如果你改变 自我::() static::bar() 在上面的示例中,它在php 5.3及更高版本中运行良好。你可以阅读更多关于 自己 VS 静止的 New self vs. new static .

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

    我想,你还是可以提出保留警告的理由。例如,在我上面的示例中,您可能会争辩说,由于PHP允许您调用抽象类的静态方法(即使在通过替换来修复它之后) 自己 具有 静止的 )你在公开一个公共方法 ParentClass::foo() 哪个是 破碎的 你真的不想曝光。使用一个非静态类——也就是说,使所有方法都成为实例方法,并使 家长班 所有这些都是单件的或是其他的东西-可以解决这个问题,因为 家长班 是抽象的,无法实例化,因此无法调用其实例方法。我认为这个论点是软弱的(因为我认为暴露 父类::foo() 这没什么大不了的,使用单例类代替静态类通常是不必要的冗长和丑陋的,但是你可能会有理由不同意——这是一个有点主观的要求。

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

    休斯敦大学, not exactly .

    上面链接的php bug报告53081要求在添加 static::foo() 构造使抽象静态方法合理实用。RasmusLerdorf(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(),但静态::b()。 很好。

    Rasmus声称他的示例中的代码“工作正常”是错误的;正如您所知,它抛出了一个严格的模式警告。我猜他在测试时没有打开严格的模式。不管怎样,一个困惑的拉斯穆斯将请求错误地关闭为“伪造”。

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

    幸运的是,可估计的nikita popov已经从php 7语言中删除了警告,作为 PHP RFC: Reclassify E_STRICT notices . 最终,理智占了上风,一旦php 7发布,我们都可以很高兴地使用它。 抽象静态 没有收到这个愚蠢的警告。

        3
  •  70
  •   Wouter van Vliet    13 年前

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

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

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

    <?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来创建具有类似功能的类。

        4
  •  12
  •   Petah    14 年前

    我知道这是旧的,但是……

    为什么不直接抛出一个异常这个父类的静态方法,如果不重写它,就会导致异常。

        5
  •  4
  •   merkuro    15 年前

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

        6
  •  2
  •   user2964021    11 年前

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

        7
  •  0
  •   kamil    10 年前

    在php 5.4+中,使用特性:

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

    在你们班上,把乞讨的话说出来:

    use StaticExample;
    
        8
  •  -1
  •   Sean McSomething    15 年前

    研究PHP的“后期静态绑定”问题。如果您将静态方法放在抽象类上,您可能会很快遇到它,而不是很晚。严格的警告告诉您避免使用中断的语言功能是有意义的。