1

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?

3
  • 2
    How about you not use global variables at all? Commented Aug 21, 2018 at 9:50
  • @tereško that's the programmers spirit i was hoping for Commented Aug 21, 2018 at 10:29
  • I am actually serious. If you use a "static class" it's actually just a list of namespaced global functions (because, before 5.3 there was no namespacing) and the static variables are essentially just an alternative form of global state. Commented Aug 21, 2018 at 10:32

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

hey @iainn, thx for the answer! I was using the static method var for singletons: tehplayground.com/Wob6CNZF8wtm3Iue For this behaivour, static method var seemed the only solution.
Don't use static, don't use singletons. Stop this madness. Embrace dependency injection.
@emix singletons vs DI <==> apples vs pears
Sorry, but no. Write proper OOP, SOLID, testable, maintainable code.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.