Page 1 of 1

[SOLVED] Weird static variable behavior

Posted: Tue Jul 03, 2007 8:36 am
by msimoes
Hy,

I'm developing an application where I'm getting a weird behavior from the static variable:

-----------------------------------------------------------

Code: Select all

   public function &getConfig( $file = NULL , $type = 'default' ) {
     static $instance;

     if( is_null( $file ) ) $file = myBasePath . DS . 'myConfiguration.php';
     if( !is_object( $instance ) ) {
       $instance =& self::_createConfig( $file , $type );
     }
     return $instance;
   }
-----------------------------------------------------------

I call this function using something like $var =& class::getConfig();

While calling it the first time, the values are stored correctly within the $instance ( since its built by another method within the class ).


After that, I do another $var =& class::getConfig() where I intend to return $instance has a reference so I can update the values within it and have it avaiable at other functions/methods/whatever...

But here the weird behavior starts... while I'm in the method/function where I update the $var ( reference of $instance ) I can get the updated values stored at $instance, as soon as I leave to another method/function, the $instance looses the changes and only stores the values created
at the first call ( a simples example of a var_dump() ):

1. First call ( method/function 1 ): stored values
2. Second call ( method/function 2 where I update $instance ): stored values and updated ones
3. Third call ( method/function 3 ): only stored values again and the new ones ( inserted at method/function 2 ) are lost...

I've searched around and no answer was found about this strange behavior ( even tought I'm returning refernce it behaves like returning only values )... so I did some "bad coding" and I came up with this function:

-----------------------------------------------------------

Code: Select all

    public function &getConfig( $file = NULL , $type = 'default' ) {
     static $instance;

     if( is_null( $file ) ) $file = myBasePath . DS . 'myConfiguration.php';
     if( !is_object( $instance ) ) {
       $tmp =& self::_createConfig( $file , $type );
       unset( $instance );
       static $instance;
       $instance = $tmp;
     }
     return $instance;
   }
-----------------------------------------------------------

As stupid as this code may seem, it works, and now the "var_dump()" would result in something like:

1. First call ( method/function 1 ): stored values
2. Second call ( method/function 2 where I update $instance ): stored values and updated ones
3. Third call ( method/function 3 ): stored values and the new values ( inserted at the function/method 2 ) are still there, and no data was lost.


Can someone explain me whats the reason for this strange behavior ?


Best regards,
Miguel Simões

Posted: Tue Jul 03, 2007 12:03 pm
by Ambush Commander
You're mixing static variables and references together. When you use $instance =&, you're assigning a reference to the $instance variable, which is overriding the static declaration. You'll notice in your second set of code you are simply writing "$instance = $tmp".

Quite simply, you don't need to assign the initial creation by reference, because it is a reference to the static variable you want.

Code: Select all

public function &getConfig( $file = NULL , $type = 'default' ) {
     static $instance;

     if( is_null( $file ) ) $file = myBasePath . DS . 'myConfiguration.php';
     if( !is_object( $instance ) ) {
       $instance = self::_createConfig( $file , $type );
     }
     return $instance;
   }

Posted: Tue Jul 03, 2007 12:15 pm
by msimoes
Thanks for the reply, it really resolved the issue.

I wasn't aware that the reference would overwrite the static declaration... since when doing $instance = $tmp, $tmp would also be a reference and not a value... ( if I make myself clear ).


Best regards,
Miguel Simões

Posted: Tue Jul 03, 2007 3:29 pm
by Ambush Commander
I wasn't aware that the reference would overwrite the static declaration... since when doing $instance = $tmp, $tmp would also be a reference and not a value... ( if I make myself clear ).
It won't overwrite the "static declaration" per-say, instead, the reference will be changed to point to another value that's not static.

An illustration: imagine we have two memory registers (you can call them variables, but then we'll have a little vocabulary confusion later on). These two registers are the variable that has been made static, and the variable that self::_createConfig returns (presumably it is a variable, otherwise you wouldn't be able to assign the function's return value by reference).

When you declare static $instance; you make a reference in $instance to the static memory register. Think of it as an arrow pointing to the register. You can change the value in $instance = 'foo'; the static memory register will be changed accordingly, you can return it by reference, etc. This also means that if you let $instance = null you cleared out the static memory register and if you unset($instance) you simply de-referenced $instance: the static memory register still remains. This can easily be demonstrated by running the following code:

Code: Select all

function test() {
    static $foo; // create reference
    print_r($foo); // print its value, first time it's null
    $foo = 'asdf'; // assign value to reference
    unset($foo); // destroy reference, BUT NOT VALUE
}
test(); // no output
test(); // 'asdf'
Now, when you let $instance =& self::_createConfig(), you are overriding the original reference to the static memory register to the self::_createConfig register. $foo is no longer a static variable, a reference to the static memory register; it is a reference to the variable returned by the function call. It no longer points to the static register, it points to the function's returned variable.

To answer your newest question, when you let $instance = $tmp, the de-referencing happens automatically and $instance (and the memory register which it is pointing to) is written with the value of $tmp; the reference pointer is not changed, the location of the memory register is not changed to that of $tmp. To get that behavior, you must say $instance =& $tmp (and then the code would stop working again).

Posted: Tue Jul 03, 2007 7:39 pm
by msimoes
Thankx for the clarification... this was starting to be an issue which I was almost giving up.

I never looked at the static $instance; as being a "reference to the memory"... looking at it this way, makes sense that making $instance =& $var; would break it and virtually making it work as a "value return".

Sometimes, to resolv an issue its necessary to have a different point of view. While using other languages I was able to work with a similar code like the one returning the error, but looking at it the way you described makes sense that it doesn't work.


Again, thankx for the help :)


Best regards,
Miguel Simões

Posted: Thu Jul 05, 2007 7:03 am
by Ambush Commander
I've always found the most insidious problems when learning a different language is when something looks the same, but actually functions differently. In such cases, it's quite helpful to have read the manual and know precisely what's going on.