Accessing incorrect static property from within non static method in PHP

164 Views Asked by At

I am experiencing something in PHP that seems very odd. I am trying to access a static property from a non static method. I need to use the static keyword to access this property as it can have different values in each child class.

However, instead of accessing a property from the expected class at all, it instead accesses the property from the calling class. This seems like a bug to me, but if it isn't, I was wondering if someone could explain this behaviour to me and also explain how I can access this static property.

My expectation here is that the static property $why would be taken from class B. I am perplexed as to why instead it would be taken from Class A.

<?php

error_reporting(E_ALL & ~E_STRICT);

class A
{
    public static $why = "Really don't want this value. Bug?";
    public function callB()
    {
        $B = new B;
        $B::getWhy(); // PHP Bug? 
        $B->getWhy();
        $B::getWhyStatic();
        $B::getWhyStaticSelf();
    }
}

class Base {

    protected static $why = "Don't want this value";

    public static function getWhyStatic()
    {
        echo static::$why . "<BR>\n";    
    }

    public static function getWhyStaticSelf()
    {
        echo self::$why . "<BR>\n";    
    }

    public function getWhy()
    {
        echo static::$why . "<BR>\n";
    }
}

class B extends Base
{
    protected static $why = "Want this value?";
}

$A = new A;
$A->callB();
2

There are 2 best solutions below

1
On

$B::bah(); should be $B->bah();, as bah is not a static function.

7
On

This isn't really a bug, but more it's result that's not really covered in the documentation. I've done a bit of research and played with a few things myself and I think I've figured it out, but I can't be 100% certain as there's no official text covering this.

The impression that I get, is when you attempt to call a static method on an instance, it's the equivelant of using the class name, and PHP will actually call it that way, rather than of the instance. For example $B::getWhy(); is the same as B::getWhy(), which is how the core code will see it, whether you pass it an instance or not.

The issue you're having is that you're calling a non-static method as a static method. Because of the way methods work, they require a scope to provide such things as self and $this. Now, by calling the non-static method as if it was a static method, and taking into consideration the above about the way PHP will actually run the code, the only scope that is available is that of class A because that's the scope you're calling it from. This means that the late static binding takes place and overrides B::$why with A::$why because of the scope change, which is exactly what late static binding is supposed to achieve.

I hope that makes sense, if there are any bits that are unclear, let me know and I'll do my best to explain.

For more information, there's another question that actually tackles this: Calling non static method with "::"