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:
Strict Standards: Static function Program::getSelectSQL() should not be abstract in Program.class.inc
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.
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.
My question is: can someone explain in a clear way why there shouldn't be an abstract static function in PHP?
It's a long, sad story.
When PHP 5.2 first introduced this warning, late static bindings weren'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
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()".
Given this, abstract static methods in PHP 5.2 were useless. The entire point of 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 static
in PHP 5.2 is bad code, probably inspired by a misunderstanding of how the self
keyword works. It was entirely reasonable to throw a warning over this.
But then PHP 5.3 came along added in the ability to refer to the class on which a method was called via the static
keyword (unlike the self
keyword, 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 self
vs static
at New self vs. new static.
With the static keyword added, the clear argument for having abstract static
throw 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.
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 self
with static
) you're exposing a public method ParentClass::foo()
which is broken and 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 ParentClass
all 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.
So based upon this argument, the PHP devs kept the warning in the language, right?
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:
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.
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".
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 static
without receiving this silly warning.