Friedrich Roell
Friedrich Roell

Reputation: 457

static function variable vs static class variable

Consider the following:

class Super
{
    static protected $class_var = 0;

    public function __construct()
    {
        static $function_var = 0;

        static::$class_var++;
        $function_var++;

        echo sprintf("class_name : %s, class_var : %s, function_var : %s\n", static::class, static::$class_var, $function_var);
    }

    public static function test()
    {
        static $function_var = 0;

        static::$class_var++;
        $function_var++;

        echo sprintf("class_name : %s, class_var : %s, function_var : %s\n", static::class, static::$class_var, $function_var);
    }
}

class A extends Super {}
class B extends Super {}

echo "Object calls\n";
new A();
new A();
new B();
echo "Class calls\n";
A::test();
A::test();
B::test();

PHP 7.2.7 ouputs:

Object calls
class_name : A, class_var : 1, function_var : 1
class_name : A, class_var : 2, function_var : 2
class_name : B, class_var : 3, function_var : 3
Class calls
class_name : A, class_var : 4, function_var : 1
class_name : A, class_var : 5, function_var : 2
class_name : B, class_var : 6, function_var : 1

Why do A and B have their own static function var when called on the class. Whereas A and B share the static function var when called on instances. Or more general, how do they exactly work internally?

Upvotes: 1

Views: 113

Answers (1)

iainn
iainn

Reputation: 17417

The general behaviour can be explained like this (there's a proviso below about constructors): static class properties are scoped against the class in which they are defined, and static method variables are scoped against the class for which they are called.

The fact your test method is declared as static isn't important here, so I've cut this down into the following test script:

class Super
{
    static protected $class_var = 0;

    public function test()
    {
        static $function_var = 0;

        static::$class_var++;
        $function_var++;

        echo sprintf("class_name : %s, class_var : %s, function_var : %s", static::class, static::$class_var, $function_var), PHP_EOL;
    }
}

class A extends Super {}
class B extends Super {}

Because it's defined on the class, $class_var will always be linked to that scope. However many classes extend Super, they'll refer to the same variable (unless they override it).

$function_var on the other hand, is scoped to the class(es) in which the test method is called. Calls to all instances of A will share one, and calls to all instances of B will share one.

$a = new A;
$b = new B;

$a->test(); // class_name : A, class_var : 1, function_var : 1
$a->test(); // class_name : A, class_var : 2, function_var : 2
$b->test(); // class_name : B, class_var : 3, function_var : 1

Constructors (A Proviso):

The other issue in your question is that the constructor behaves differently to any other class method, because despite being syntactically similar, it isn't a method at all. Similar to class properties, any variable declared static in the constructor is scoped to the class in which it's defined:

If we add in:

public function __construct()
{
    static $constructor_var = 0;
    $constructor_var++;

    echo sprintf("class_name : %s, constructor_var : %s", static::class, $constructor_var), PHP_EOL;
}

Then we can demonstrate as follows:

$a = new A; // class_name : A, constructor_var : 1
$b = new B; // class_name : B, constructor_var : 2

Unless any of the subclasses override the constructor, they'll all share the same instance of $constructor_var.

I can't find a nice simple summary of any of this in the documentation unfortunately, and I'm also not sure how much of it is by design at all. Static variables in a constructor especially aren't something I think I've ever come across, and static function variables are increasingly rare themselves. But it's interesting to know how things work, so good question.

Upvotes: 2

Related Questions