Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does PHP allow abstract static functions

Consider the following code:

abstract class ExampleClass
{
    public static function regularStaticFunction()
    {
        return static::abstractStaticFunction();
    }

    abstract protected static function abstractStaticFunction();
}

ExampleClass::regularStaticFunction();

The PhpStorm IDE puts a warning on the declaration of abstractStaticFunction that reads:

PHP Strict Standards: Static function 'abstractStaticFunction' should not be abstract.

Static function should not be abstract.

However, PHP continues program execution when parsing this class and outputs the following:

PHP Strict standards: Static function ExampleClass::abstractStaticFunction() should not be abstract in php shell code on line 7

It seems to me that because PHP allows static function calls on abstract classes, defining an abstract static function on an abstract class should not be possible.

Why are abstract static functions allowed in PHP by the interpreter, when they are nonsensical?

like image 256
Thijs Riezebeek Avatar asked Jan 12 '17 10:01

Thijs Riezebeek


2 Answers

This is good explanation from this answer by Mark Amery:

PHP bug report 53081, 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.

like image 142
German Lashevich Avatar answered Nov 16 '22 11:11

German Lashevich


Abstract static functions aren't nonsensical! In fact, they enable some designs that are, to my sensibilities, simpler and cleaner than what I'd have to resort to in a language that doesn't have them, like Java or C#.

Let's take an example. Suppose I'm writing some sort of mundane enterprisey business application that needs to synchronise some business objects between two APIs. These APIs have object models that can be mapped to each other, but use different names, and different serialisation formats.

In PHP, thanks to abstract static methods, I can define an abstract base class for these business object types that looks like this...

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

... and then every concrete subclass I create will be guaranteed to provide all the information needed to fetch it from either of the two APIs and deserialise it, or to serialize it and send it to either API. Then, later, I can write some code like this:

// 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);
    }
}

Do I strictly need abstract static methods to write the program above? No; there are other patterns I could've used, like having every model class be paired with a corresponding factory class that is responsible for instantiating it from its JSON or XML representations. But such a design is more complicated than the one I've shown above.

The answer to why they're allowed, then, is simply that they are useful, since they enable some nice, simple patterns that wouldn't be possible without them. Of course, there are also arguments against them existing, one of which was given in the question - that it's ugly to expose abstract static methods on a class given that static methods on an abstract class are callable. I don't find such considerations particularly persuasive, but even if you do, there is still a tradeoff between them and the utility that abstract static methods provide, and the PHP maintainers have presumably weighed them and opted for the side of letting abstract static methods exist.

like image 28
Mark Amery Avatar answered Nov 16 '22 10:11

Mark Amery