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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/999066/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 00:36:00  来源:igfitidea点击:

Why does PHP 5.2+ disallow abstract static class methods?

phpoopstaticabstract

提问by Artem Russakovskii

After enabling strict warnings in PHP 5.2, I saw a load of strict standards warnings from a project that was originally written without strict warnings:

在 PHP 5.2 中启用严格警告后,我看到了来自一个最初没有严格警告的项目的严格标准警告:

Strict Standards: Static functionProgram::getSelectSQL() should not be abstractin Program.class.inc

严格标准:Program.class.inc 中的静态函数Program::getSelectSQL()不应该是抽象

The function in question belongs to an abstract parent class Program and is declared abstract static because it should be implemented in its child classes, such as TVProgram.

有问题的函数属于抽象父类 Program 并声明为抽象静态,因为它应该在其子类中实现,例如 TVProgram。

I did find references to this change here:

我确实在这里找到了对此更改的引用:

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 5.0.x 和 5.1.x 允许在类中使用抽象静态函数。从 PHP 5.2.x 开始,只有接口可以拥有它们。

My question is: can someone explain in a clear way why there shouldn't be an abstract static function in PHP?

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

采纳答案by Jonathan Fingland

static methods belong to the class that declared them. When extending the class, you may create a static method of the same name, but you are not in fact implementing a static abstract method.

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

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

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

EDIT(Sept. 16th, 2009)
Update on this. Running PHP 5.3, I see abstract static is back, for good or ill. (see http://php.net/lsbfor more info)

编辑(2009 年 9 月 16 日)
对此进行更新。运行 PHP 5.3,我看到抽象静态又回来了,无论好坏。(有关更多信息,请参阅http://php.net/lsb

CORRECTION(by philfreo)
abstract staticis still not allowed in PHP 5.3, LSBis related but different.


abstract staticPHP 5.3 中仍然不允许更正(由 philfreo),LSB相关但不同。

回答by Mark Amery

It's a long, sad story.

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

When PHP 5.2 first introduced this warning, late static bindingsweren't yet in the language. In case you're not familiar with late static bindings, note that code like this doesn't work the way you might expect:

当 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();

Leaving aside the strict mode warning, the code above doesn't work. The self::bar()call in foo()explicitly refers to the bar()method of ParentClass, even when foo()is called as a method of ChildClass. If you try to run this code with strict mode off, you'll see "PHP Fatal error: Cannot call abstract method ParentClass::bar()".

撇开严格模式警告不谈,上面的代码不起作用。该self::bar()呼叫foo()明确提到bar()的方法ParentClass,即使在foo()被称为的方法ChildClass。如果您尝试在关闭严格模式的情况下运行此代码,您将看到“ PHP 致命错误:无法调用抽象方法 ParentClass::bar()”。

Given this, abstract static methods in PHP 5.2 were useless. The entire pointof using an abstract method is that you can write code that calls the method without knowing what implementation it's going to be calling - and then provide different implementations on different child classes. But since PHP 5.2 offers no clean way to write a method of a parent class that calls a static method of the child class on which it is called, this usage of abstract static methods isn't possible. Hence any usage of abstract staticin PHP 5.2 is bad code, probably inspired by a misunderstanding of how the selfkeyword works. It was entirely reasonable to throw a warning over this.

鉴于此,PHP 5.2 中的抽象静态方法是无用的。使用抽象方法的全部意义在于,您可以编写调用该方法的代码,而无需知道它将调用什么实现——然后在不同的子类上提供不同的实现。但是由于 PHP 5.2 没有提供干净的方法来编写父类的方法,该方法调用调用它的子类的静态方法,因此抽象静态方法的这种用法是不可能的。因此,abstract static在 PHP 5.2 中的任何使用都是糟糕的代码,可能是受到了对self关键字工作方式的误解的启发。对此发出警告是完全合理的。

But then PHP 5.3 came along added in the ability to refer to the class on which a method was called via the statickeyword (unlike the selfkeyword, which always refers to the class in which the method was defined). If you change self::bar()to static::bar()in my example above, it works fine in PHP 5.3 and above. You can read more about selfvs staticat New self vs. new static.

但随后 PHP 5.3 加入了通过static关键字来引用调用方法的类的能力(与self关键字不同,它总是引用定义方法的类)。如果您在我上面的示例中更改self::bar()static::bar(),则它在 PHP 5.3 及更高版本中工作正常。您可以在New self vs. new static 中阅读有关selfvs 的更多信息。static

With the static keyword added, the clear argument for having abstract staticthrow a warning was gone. Late static bindings' main purpose was to allow methods defined in a parent class to call static methods that would be defined in child classes; allowing abstract static methods seems reasonable and consistent given the existence late static bindings.

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

You could still, I guess, make a case for keeping the warning. For instance, you could argue that since PHP lets you call static methods of abstract classes, in my example above (even after fixing it by replacing selfwith static) you're exposing a public method ParentClass::foo()which is brokenand that you don't really want to expose. Using a non-static class - that is, making all the methods instance methods and making the children of ParentClassall be singletons or something - would solve this problem, since ParentClass, being abstract, can't be instantiated and so its instance methods can't be called. I think this argument is weak (because I think exposing ParentClass::foo()isn't a big deal and using singletons instead of static classes is often needlessly verbose and ugly), but you might reasonably disagree - it's a somewhat subjective call.

我想,您仍然可以提出保留警告的理由。例如,你可能会说,因为PHP可以让你叫抽象类的静态方法,我在上面的例子(甚至通过更换固定之后selfstatic你暴露的公共方法)ParentClass::foo()打破,并且你真的不想要暴露。使用非静态类——也就是说,使所有方法成为实例方法,并使所有方法的子类ParentClass成为单例或其他东西——将解决这个问题,因为ParentClass抽象的,不能被实例化,所以它的实例方法不能叫做。我认为这个论点很弱(因为我认为暴露ParentClass::foo()没什么大不了的,使用单例代替静态类通常是不必要的冗长和丑陋),但您可能有理由不同意 - 这是一个有点主观的调用。

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

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

Uh, not exactly.

呃,不完全是

PHP bug report 53081, linked above, called for the warning to be dropped since the addition of the static::foo()construct had made abstract static methods reasonable and useful. Rasmus Lerdorf (creator of PHP) starts off by labelling the request as bogus and goes through a long chain of bad reasoning to try to justify the warning. Then, finally, this exchange takes place:

上面链接的 PHP 错误报告 53081 要求删除警告,因为添加static::foo()构造使抽象静态方法变得合理和有用。Rasmus Lerdorf(PHP 的创建者)首先将请求标记为虚假请求,然后通过一长串糟糕的推理来试图证明警告的合理性。然后,最后,这种交换发生:

Giorgio

i know, but:

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.

Giorgio

but it is not allowed :(

Rasmus

What's not allowed?

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.

乔治

我知道但是:

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".

Rasmus 声称他的示例中的代码“工作正常”是错误的;如您所知,它会引发严格模式警告。我猜他是在没有打开严格模式的情况下进行测试的。无论如何,困惑的拉斯穆斯将请求错误地关闭为“虚假”。

And that's why the warning is still in the language. This may not be an entirely satisfying explanation - you probably came here hoping there was a rational justification of the warning. Unfortunately, in the real world, sometimes choices are born from mundane mistakes and bad reasoning rather than from rational decision-making. This is simply one of those times.

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

Luckily, the estimable Nikita Popov has removed the warning from the language in PHP 7 as part of PHP RFC: Reclassify E_STRICT notices. Ultimately, sanity has prevailed, and once PHP 7 is released we can all happily use abstract staticwithout receiving this silly warning.

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

回答by Wouter Van Vliet

There is a very simple work around for this issue, which actually makes sense from a design point of view. As Jonathan wrote:

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

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

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

So, as a work around you could do this:

因此,作为解决方法,您可以这样做:

<?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();
}
?>

And now you enforce that any class subclassing MyFoo implements a getInstance static method, and a public getSomeData method. And if you don't subclass MyFoo, you can still implement iMyFoo to create a class with similar functionality.

现在你强制任何继承 MyFoo 的类都实现一个 getInstance 静态方法和一个公共 getSomeData 方法。如果你不继承 MyFoo,你仍然可以实现 iMyFoo 来创建一个具有类似功能的类。

回答by Petah

I know this is old but....

我知道这是旧的但是......

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.

为什么不只是在该父类的静态方法中抛出异常,这样如果不覆盖它就会导致异常。

回答by merkuro

I would argue that an abstract class/interface could be seen as a contract between programmers. It deals more with how things should look/ behave like and not implement actual functionality. As seen in php5.0 and 5.1.x it's not a natural law that prevents the php developers from doing it, but the urge to go along with other OO design patterns in other languages. Basically these ideas try to prevent unexpected behavior, if one is already familiar with other languages.

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

回答by user2964021

I don't see any reason to forbid static abstract functions. The best argument that there is no reason to forbid them is, that they are allowed in Java. The questions are: - Are the technically feasable? - Yes, since the existed in PHP 5.2 and they exist in Java. So whe CAN do it. SHOULD we do it? - Do they make sense? Yes. It makes sense to implement an part of a class and leave another part of a class to the user. It makes sense in non-static functions, why shouldn't it make sense for static functions? One use of static functions are classes where there must not be more than one instance (singletons). For example an encryption engine. It does not need to exist in several instances and there are reasons to prevent this - for example, you have to protect only one part of the memory against intruders. So it makes perfect sense to implement one part of the engine and leave the encryption algorithm to the user. This is only one example. If you are accustomed to use static functions you'll find lots more.

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

回答by kamil

In php 5.4+ use trait:

在 php 5.4+ 中使用特性:

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

and in your class put at the beggining:

并在你的班级开始:

use StaticExample;

回答by Sean McSomething

Look into PHP's 'Late Static Binding' issues. If you're putting static methods on abstract classes, you're probably going to run into it sooner rather than later. It makes sense that the strict warnings are telling you to avoid using broken language features.

查看 PHP 的“后期静态绑定”问题。如果您将静态方法放在抽象类上,您可能迟早会遇到它。严格警告告诉您避免使用损坏的语言功能是有道理的。