php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #72916 get_called_class is wrong after calling via self:: from non-static method
Submitted: 2016-08-21 23:18 UTC Modified: 2017-04-12 21:36 UTC
From: gharlan at web dot de Assigned:
Status: Not a bug Package: Class/Object related
PHP Version: 7.0.10 OS:
Private report: No CVE-ID: None
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: gharlan at web dot de
New email:
PHP Version: OS:

 

 [2016-08-21 23:18 UTC] gharlan at web dot de
Description:
------------
The return value of get_called_class() is wrong, when it is called from a static method that is called via self::method() in a non-static method.

Test script:
---------------
<?php
class Foo {
    protected static function getClass() {
        return get_called_class();
    }
    
    public function bar() {
        echo 'Foo: ', Foo::getClass(), "\n";
        echo 'self: ', self::getClass(), "\n";
        echo 'static: ', static::getClass(), "\n";
    }
}

class Child extends Foo {
    protected static function getClass() {
        return 'x';
    }
}

(new Child)->bar();

Expected result:
----------------
Foo: Foo
self: Foo
static: x

Actual result:
--------------
Foo: Foo
self: Child
static: x

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-04-12 19:37 UTC] mail at pmmaga dot net
Is it possible you are expecting the behavior of get_class instead of get_called_class?

From the documentation, get_called_class: "Gets the name of the class the static method is called in."

In your example, the result "Child" follows that rule.
 [2017-04-12 21:36 UTC] requinix@php.net
-Status: Open +Status: Not a bug
 [2017-04-12 21:36 UTC] requinix@php.net
I was having a hard time deciding whether this is a bug until I went to the LSB docs to see what they said.
http://php.net/manual/en/language.oop5.late-static-bindings.php

LSB works by
> storing the class named in the last "non-forwarding call"
so the key here is to understand what a forwarding call is:
> A "forwarding call" is a static one that is introduced by self::, parent::, static::,

That means the calls in the stack are
  (new Child)->bar(); // not a forwarded call - LSB->Child
  self::getClass();   // forwarded call - LSB unchanged

Thus get_called_class() should return Child.

"What about Foo::getClass?"
Foo:: is not a forwarding call so LSB begins tracking Foo.

"Okay, but what about static::getClass?"
Before LSB there was no way to get "inheritance" with static calls: self:: would not go down the inheritance hierarchy, remaining only in the current class or going to an ancestor. LSB and its static:: was the solution.

What made static:: different from self:: was that
> static:: will not be resolved using the class where the method is defined but
> it will rather be computed using runtime information

That "runtime information" would be the LSB class, thus static::getClass() will call Child::getClass().

There's an important distinction to be made here: LSB and its forwarded call tracking are separate mechanisms from class resolution done through ::. When you use self::method() or static::method() the LSB class is unchanged, however the two will (potentially) resolve to and call different method()s.
In other words, the internal state is the same but the actual code being executed next differs, and what get_called_class() does is give you a peek into that internal state.

So in that sense the "static" in late-static bindings could be misleading. It's not about when the first static call was made but about tracking the first class invoked directly (ie, via an instance or its name) and then *using that* with future static:: calls.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 12:01:31 2024 UTC