Why does PHP 5.2+ disallow abstract static class methods?

66,324

Solution 1

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/lsb for more info)

CORRECTION (by philfreo)
abstract static is still not allowed in PHP 5.3, LSB is related but different.

Solution 2

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.

Solution 3

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.

Solution 4

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.

Solution 5

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.

Share:
66,324
Artem Russakovskii
Author by

Artem Russakovskii

http://twitter.com/ArtemR

Updated on March 26, 2020

Comments

  • Artem Russakovskii
    Artem Russakovskii about 4 years

    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?

  • Artem Russakovskii
    Artem Russakovskii almost 15 years
    OK, so what if I wanted to enforce the need for function getSelectSQL() in all children that extend my abstract class? getSelectSQL() in the parent class has no valid reason to exist. What's the best plan of action? The reason I chose abstract static is that the code wouldn't compile until I've implemented getSelectSQL() in all children.
  • Matthew Flaschen
    Matthew Flaschen almost 15 years
    Most likely, you should redesign things so getSelectSQL() is a abstract /instance/ method. That way, /instances/ of each child will have such a method.
  • Pradeep Kumar
    Pradeep Kumar almost 15 years
    that is probably not the best way to go about it. Essentially, you're trying to take advantage of polymorphism, which in php is very easy to abuse as types are not strictly enforced (new php 5.3 features aside). However, is what you're looking for really required to be static? is it related to the object you are passing or the whole class from which it is instantiated?
  • Pradeep Kumar
    Pradeep Kumar almost 15 years
    that last one was @Artem, not Matt. @Matt Completely agree
  • merkuro
    merkuro almost 15 years
    Although not php related here is another good explanation: stackoverflow.com/questions/3284/…
  • Artefacto
    Artefacto almost 14 years
    Abstract static is still disallowed in PHP 5.3. Late static bindings have nothing to do with it. See also stackoverflow.com/questions/2859633
  • Theodore R. Smith
    Theodore R. Smith about 13 years
    My god! The two people who downvoted you are totally out of their minds! This is the most insightful answer on this thread.
  • B T
    B T over 12 years
    That doesn't help, the exception would happen on call of the static method - the same time a 'method does not exist' error would come up if you don't override it.
  • Petah
    Petah over 12 years
    @BT I meant, don't declare the method abstract, implement it, but just throw an exception when it is called, which means it wont throw if its been overridden.
  • Frank Nocke
    Frank Nocke over 12 years
    Same in Java, btw. There are good reasons (the whole OOP thing gets distorted) but hard to explain in short. Jonathan's reply is pretty good.
  • Jacob Hume
    Jacob Hume about 12 years
    I think he means the "Strict Standards" warnings.
  • artfulrobot
    artfulrobot over 11 years
    Is it possible with this pattern to make the function protected. When I do it, it means the classes that extend MyFoo throw warnings that getInstance must be public. And you can't put protected in an interface definition.
  • dmitry
    dmitry about 11 years
    In my opinion this strict warning is just stupid since PHP has "late static binding", which naturally provides idea of using static methods as if class itself had been an object (like, say, in ruby). Which leads to static method overloading and abstract static may be useful in this case.
  • seyed
    seyed about 10 years
    some times static:: can be useful.
  • Rudie
    Rudie about 10 years
    Doesn't work with Traits. If only Traits could have abstract static methods, without PHP bitching....
  • ahoffner
    ahoffner almost 10 years
    This answer is vaguely wrong. "still not allowed" just means you'll get a E_STRICT level warning, at least in 5.3+ you're perfectly welcome to create abstract static functions, implement them in extended classes, and then refer to them via the static:: keyword. Obviously the parent class' static version is still there and can't be called directly (via self:: or static:: inside that class) as it's abstract and will fatally error as if you called a regular non-static abstract function. Functionally this is useful, I agree with @dmitry sentiments to that effect.
  • Alex S
    Alex S over 9 years
    This seems to be the most elegant solution.
  • Juan Carlos Coto
    Juan Carlos Coto about 9 years
    The use of an interface is probably the best solution here. +1.
  • Programster
    Programster about 9 years
    I was able to put abstract public static function get_table_name(); in a trait and use that trait within my abstract class with no more E_STRICT warnings! This still enforced the defining of the static method in the children as I had hoped. Fantastic!
  • Mark Amery
    Mark Amery almost 9 years
    What "issues" do you claim that late static bindings have? You assert that they're "broken", which is a bold claim, made here without evidence or explanation. The feature has always worked fine for me, and I think this post is nonsense.
  • Mark Amery
    Mark Amery almost 9 years
    @TheodoreR.Smith Insightful? It contains errors and is barely coherent. The claim that abstract classes do "not implement actual functionality" is not necessarily true, and that's the entire point of them existing in addition to interfaces. In the claim that "it's not a natural law that prevents the php developers from doing it", I have no idea what "it" is. And in the claim that "Basically these ideas try to prevent unexpected behavior", I have no idea what either "these ideas" or the potential "unexpected behaviour" are. Whatever insight you extracted from this, it's lost on me.
  • G. Stewart
    G. Stewart almost 9 years
    This is actually very elegant because of its sheer simplicity. +1
  • Mark Amery
    Mark Amery almost 9 years
    -1 for the passage you've quoted from Jonathan; it ceased to be true with the release of PHP 5.3 (and addition of Late Static Bindings) in 2009, and was well out of date by the time you posted this answer.
  • Mark Amery
    Mark Amery almost 9 years
    Your point about abstract static methods existing in 5.2 is heavily misleading. Firstly, the strict mode warning forbidding them was introduced in 5.2; you make it sound like in 5.2 they were allowed. Secondly, in 5.2 they couldn't easily be used "to implement an part of a class and leave another part of a class to the user" because Late Static Bindings didn't yet exist.
  • frankish
    frankish almost 8 years
    There is a problem with this solution. You cannot set private or public methods inside interface.
  • Rahly
    Rahly over 6 years
    Better to see something like this at compile time, than run time. Easier to find problem before they are in production because you only have to load the file, not execute code to figure out if its bad or nonconforming
  • Jakob Alexander Eichler
    Jakob Alexander Eichler over 6 years
    But as there is a the self:: keyword it makes sense? Was ist not the case that self:: also helped to use inheritance with abstract static functions?
  • ahnbizcad
    ahnbizcad over 4 years
    @dmitry you had me at "In my opinion this strict warning is just stupid since PHP "
  • Michael Tsang
    Michael Tsang almost 4 years
    In PHP 7 the strict standard is removed - it is now perfectly legal under E_ALL.