Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static variable inside function can't hold reference to singleton

I've noticed a weird behavior with singletons in PHP there's no better way to explain this but with an example.

Let's say I have the following singleton class:

class Singleton
{
    protected function __construct()
    {
        // Deny direct instantion!
    }
    protected function __clone()
    {
        // Deny cloning!
    }
    public static function &Instance()
    {
        static $Instance;

        echo 'Class Echo'.PHP_EOL;
        var_dump($Instance);

        if (!isset($Instance)) {
            $Instance = new self;
        }

        return $Instance;
    }
}

And the following function:

function Test($Init = FALSE)
{
    static $Instance;

    if ($Init === TRUE && !isset($Instance)) {
        $Instance =& Singleton::Instance();
    }

    echo 'Function Echo'.PHP_EOL;
    var_dump($Instance);

    return $Instance;
}

And when I use the following:

Test(TRUE);
Test();
Singleton::Instance();

The output is:

Class Echo
NULL
Function Echo
object(Singleton)#1 (0) {
}
Function Echo
NULL
Class Echo
object(Singleton)#1 (0) {
}

As you can see the saved reference inside the function is lost after the execution even though the variable is static Also notice that the static variable inside the class method is working fine.

Is this supposed to be normal or I'm doing something wrong?

like image 216
SLC Avatar asked Feb 15 '23 17:02

SLC


1 Answers

This behaviour is documented:

The Zend Engine 1, driving PHP 4, implements the static and global modifier for variables in terms of references. For example, a true global variable imported inside a function scope with the global statement actually creates a reference to the global variable. This can lead to unexpected behaviour.

This behaviour hasn't changed since ZE1 and the solution is simply to not assign a reference to a static variable, so:

$Instance = Singleton::Instance();

Update

I've simplified the issue below:

function test2($init = false, $value = null)
{
  static $test;

  if ($init) {
    $test =& $value;
  }

  var_dump($test);
}

$v = 1;

test2(true, $v);
test2();

Output:

int(1)
NULL
like image 93
Ja͢ck Avatar answered Feb 18 '23 07:02

Ja͢ck