php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #69586 Resolution of static function variables not using forwarded calling information
Submitted: 2015-05-06 15:05 UTC Modified: 2021-02-18 10:49 UTC
From: php at lvl dot fastmail dot com Assigned:
Status: Not a bug Package: Scripting Engine problem
PHP Version: 5.6.8 OS:
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please !
Your email address:
MUST BE VALID
Solve the problem:
41 + 9 = ?
Subscribe to this entry?

 
 [2015-05-06 15:05 UTC] php at lvl dot fastmail dot com
Description:
------------
I ran into some very strange behavior while experimenting with static function variables. Static variables in the function scope appear to be resolved using late static binding: the content of the variable in that function is shared amongst all instances of a class, though each subclass gets its own separate variable space.

This is pretty logical and works fine. As an example, let's create classes Base => Animal => {Cat,Dog}. Base has a function that describes the class, and caches the result in a static function variable $description. Calling describe() on the three different classes correctly gives us three different results: http://3v4l.org/Qldve

However, once you override the describe function in Animal and call parent::describe() from within, it looks like all calling information is lost: the resolution of $description in Base's function scope now always points to Animal's variable space. For example: http://3v4l.org/CcrL8

I would have expected the static function variable and get_called_class() to always refer to the same class/variable space, parent:: or not.

Overriding the function in one of the childmost classes like Dog gives an even stranger result: $description is now correctly a separate value in Cat vs Dog, but calling Animal::describe() now uses the value left by its child class Cat?! See: http://3v4l.org/61P72

Test script:
---------------
class Base {
    public static function describe() {
        echo "--> " . ($calculated = "I am a " . get_called_class() . "\n");
        
        static $cached = null;
        
        if ($cached === null) {
            $cached = $calculated;
        }
        
        return $cached;
    }
}

class Animal extends Base {
    public static function describe() {
        return 'Hello! ' . parent::describe();
    }
}

class Cat extends Animal { }
class Dog extends Animal { }

echo Cat::describe();
echo Dog::describe();
echo Animal::describe();

Expected result:
----------------
--> I am a Cat
Hello! I am a Cat

--> I am a Dog
Hello! I am a Dog

--> I am a Animal
Hello! I am a Animal

Actual result:
--------------
--> I am a Cat
Hello! I am a Cat

--> I am a Dog
Hello! I am a Cat

--> I am a Animal
Hello! I am a Cat

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-02-28 22:25 UTC] cmb@php.net
-Package: Unknown/Other Function +Package: Scripting Engine problem
 [2021-02-18 10:49 UTC] nikic@php.net
-Status: Open +Status: Not a bug
 [2021-02-18 10:49 UTC] nikic@php.net
Static variables are bound to a specific method on a specific class, and have no relation to or interaction with late static binding, nor should they.

Thus you end up caching whatever LSB scope you happen to use when you first call Animal::describe(). From this we may conclude that caching an LSB-dependent value in a static variable is ill-advised.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Mar 29 11:01:29 2024 UTC