Re: Strange behaviour of static declared content.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Stut wrote:
> Colin Guthrie wrote:
>> Stut wrote:
>>> Colin Guthrie wrote:
>>>> Stut wrote:
>>>>> Stut wrote:
>>>>>> In that case you need a new foo. That's the only way you're going to
>>>>>> reset the internal static if the API doesn't give you a way to do it.
>>>>>>
>>>>>> $f=new foo();
>>>>>> $f->bar();
>>>>>> $f->bar();
>>>>>> $g=new foo();
>>>>>> $g->bar();
>>>>> Actually, scratch that, won't work. Not even unsetting $f before
>>>>> creating the new object works. This kinda sucks since it means PHP
>>>>> does
>>>>> not support static function-scoped vars.
>>>> Yeah I tried that same thing too and then wondered if I had
>>>> misinterpreted how function-scoped statics worked.
>>>>
>>>> I've often used a method like:
>>>>
>>>> function Init()
>>>> {
>>>>  static $bln_inited = false;
>>>>  if (!$bln_inited)
>>>>  {
>>>>   // Do stuff
>>>>   $bln_inited = true;
>>>>  }
>>>> }
>>>>
>>>>
>>>> I had always assumed that the static definition here was
>>>> function-scoped... I guess I should have tested more but still it
>>>> caught
>>>> me off guard this morning when I played with it.
>>>>
>>>> Correct me if I'm wrong but does C++ not do it as both of us initially
>>>> thought? e.g. static is function scoped rather than globally scoped
>>>> when
>>>> used within a class method?
>>> Yes it does, which is why I assumed PHP would do it like that too. I
>>> knew I should have tried it before sending the reply.
>>>
>>> I've tried various ways of accessing that variable via a derived class
>>> but it doesn't seem to be possible. Again I'm just assuming, but my
>>> theory is that the line...
>>>
>>>     static $foobar = false;
>>>
>>> ...actually is static to the function and therefore cannot be accessed
>>> from outside it in any way, shape or form.
>>>
>>> So using function statics as you have above will work fine. And they
>>> also work fine when used in a method of a class. This behaviour is
>>> correct because if a function-scoped static is to work correctly there
>>> should be absolutely no way to get at it from outside the function.
>>
>> I completely agree that there should be no way to access it outside the
>> function/method it is defined within. The problem is that in PHP the
>> static keyword seems to be globally unique but function/class scoped.
>> e.g. that if it is used in a class method, regardless of how many
>> instances of that method you create with "new" there is only ever one
>> "instance" of the variable.
>>
>> So the case where it wont work fine is when you have the above function
>> as a method of a class which can have multiple instances, all of which
>> need to be Init()'ed independently.
>>
>> In such cases, I guess you'd have to use a private member variable
>> instead which just isn't quite a neat and tidy.
>>
>> If you only ever have one instance of the class then all is well but
>> still could have unexpected side effects if you later extend the system
>> to have more than one instance. I guess if you use a singltron method
>> with a private constructor then it's safe enough but then you can just
>> do your initialisation in the singletron method in that case so there's
>> little point!
>>
>> Hey ho.
>>
>> /me scuttles of to grep code for "static" :s
> 
> Nope, not globally unique: http://dev.stut.net/php/funcstatic.php
> 
> So what you have will be fine.

No it wont work as I *intended* but that is purely due to my own
misinterpretation of how statics in a method works.

I've satisfied my own understanding now by testing it in C++ and it
behaves the same as in PHP here, so I'm completely in the know (now!).

I did however think it worked differently! Like I say it's just my own
misunderstanding.


Here is what I meant for completeness:

Try the following code and note especially the last two lines of the
output (change the \n to <br /> if you want if for web output.. I tested
on cmdline).

<?php
    $GLOBALS['eol'] = "\n";
    class c
    {
        public function a($nm)
        {
            static $var = 0;
            print $nm.'a: '.$var.$GLOBALS['eol'];
            $var++;
        }

        public function b($nm)
        {
            static $var = 0;
            print $nm.'b: '.$var.$GLOBALS['eol'];
            $var++;
        }
    }


    $c = new c();
    for ($i = 0; $i < 10; $i++)
    {
        $c->a('C');
        if ($i % 2)
          $c->b('C');
    }

    $d = new c();
    $d->a('D');
    $d->b('D');

?>

Ca: 0
Ca: 1
Cb: 0
Ca: 2
Ca: 3
Cb: 1
Ca: 4
Ca: 5
Cb: 2
Ca: 6
Ca: 7
Cb: 3
Ca: 8
Ca: 9
Cb: 4
Da: 10
Db: 5


i.e. the $d object just carries on where $c left off. This is not what I
had in my head how things worked.

I had originally thought that the static keyword within a class method
was only static in the context of that *instance* of the class. This is
clearly mince now I look into it and think about it a bit.


e.g. consider the following class.


<?php
    $GLOBALS['eol'] = "\n";
    class foo
    {
        private $mVar = "Uninitialised";

        public function Init()
        {
            static $bln_initialised = false;
            if (!$bln_initialised)
            {
              $this->mVar = 'Initialised';
              $bln_initialised = true;
            }
        }

        public function Display($nm)
        {
            echo $nm.': '.$this->mVar.$GLOBALS['eol'];
        }
    }


    $foo = new foo();
    $foo->Init();
    $foo->Display('Foo');

    $bar = new foo();
    $bar->Init();
    $bar->Display('Bar');
?>


Foo: Initialised
Bar: Uninitialised



That's not what I *wanted* to happen, even tho' I now understand that's
what *should* happen!

I confirmed this with C++ to satisfy me that that's what it does too and
it does:

#include <iostream>
#include <string>
using namespace std;


class foo
{
  public:
    foo();
    void Init();
    void Display(string nm);

  private:
    string var;

};

foo::foo()
 :var("Uninitialised")
{
}

void foo::Init()
{
  static bool bln_initialised = false;
  if (!bln_initialised)
  {
    var = "Initialised";
    bln_initialised = true;
  }
}

void foo::Display(string nm)
{
  cout << nm << ": " << var << endl;
}

int main()
{
  foo foo, bar;

  foo.Init();
  foo.Display("Foo");

  bar.Init();
  bar.Display("Bar");

  return 0;
}


Foo: Initialised
Bar: Uninitialised



So my understanding suitably updated. :)

Col

PS I know the above examples are contrived and that constructors would
be more appropriate for the above - but using constructors is not always
possible due to how you deal with failed initialisations where
exceptions are not desirable.

-- 
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


[Index of Archives]     [PHP Home]     [Apache Users]     [PHP on Windows]     [Kernel Newbies]     [PHP Install]     [PHP Classes]     [Pear]     [Postgresql]     [Postgresql PHP]     [PHP on Windows]     [PHP Database Programming]     [PHP SOAP]

  Powered by Linux