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
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: php at lvl dot fastmail dot com
New email:
PHP Version: OS:

 

 [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 Apr 26 18:01:31 2024 UTC