From: Daniel Kolbo on
Richard Quadling wrote:
> On 12 July 2010 22:54, Daniel Kolbo <kolb0057(a)umn.edu> wrote:
>> Richard Quadling wrote:
>>> On 11 July 2010 23:19, Daniel Kolbo <kolb0057(a)umn.edu> wrote:
>>>> Hello PHPers,
>>>>
>>>> I'm having some trouble understanding some PHP behaviour. The following
>>>> example script exhibits the behaviour which I cannot understand.
>>>> [code]
>>>> <?php
>>>>
>>>> class A
>>>> {
>>>> public static $a = 3;
>>>>
>>>> function __construct()
>>>> {
>>>> //self::$a = $this; //[i]
>>>> self::$a =& $this; //[ii]
>>>> }
>>>> }
>>>>
>>>> class B extends A
>>>> {
>>>> function __construct()
>>>> {
>>>> parent::__construct();
>>>> }
>>>> }
>>>>
>>>> class C {
>>>> var $c;
>>>>
>>>> function __construct()
>>>> {
>>>> $this->c =& A::$a;
>>>> }
>>>>
>>>> }
>>>>
>>>>
>>>> $c = new C;
>>>> $b = new B;
>>>> $cee = new C;
>>>>
>>>> var_dump($c->c); // [i] prints object(B), but [ii] prints int 3
>>>> var_dump($cee->c); // [i] prints object(B), and [ii] prints object(B)
>>>>
>>>> ?>
>>>> [/code]
>>>>
>>>> Why does $c->c print 'int 3' ?
>>>>
>>>> I'm nervous to use "self::$a = $this;" because I don't want to be
>>>> copying the whole object. However, isn't $this just a reference to the
>>>> object, so "self::$a = $this;" is just copying the reference and not the
>>>> actual object, right?
>>>>
>>>> Thanks in advance
>>>
>>> What do you think the value should be?
>>>
>>> A static property is bound to the class and not to an instance of the class.
>>>
>>> So, &A::$a is a reference to the static value. If you alter the value,
>>> it will be altered for a subclasses of A and for any other reference
>>> to it.
>>>
>> I think
>> var_dump($c->c); would print object(B), but it's printing int 3.
>>
>> The reference is *not* being updated. I think this is a bug. What do
>> you think?
>>
>> Thanks
>> `
>>
>>
>
> What version of PHP are you using?
>
I'm using:
PHP Version 5.2.13
From: Daniel Kolbo on
Richard Quadling wrote:
> On 13 July 2010 09:46, Richard Quadling <rquadling(a)gmail.com> wrote:
>> On 12 July 2010 22:54, Daniel Kolbo <kolb0057(a)umn.edu> wrote:
>>> Richard Quadling wrote:
>>>> On 11 July 2010 23:19, Daniel Kolbo <kolb0057(a)umn.edu> wrote:
>>>>> Hello PHPers,
>>>>>
>>>>> I'm having some trouble understanding some PHP behaviour. The following
>>>>> example script exhibits the behaviour which I cannot understand.
>>>>> [code]
>>>>> <?php
>>>>>
>>>>> class A
>>>>> {
>>>>> public static $a = 3;
>>>>>
>>>>> function __construct()
>>>>> {
>>>>> //self::$a = $this; //[i]
>>>>> self::$a =& $this; //[ii]
>>>>> }
>>>>> }
>>>>>
>>>>> class B extends A
>>>>> {
>>>>> function __construct()
>>>>> {
>>>>> parent::__construct();
>>>>> }
>>>>> }
>>>>>
>>>>> class C {
>>>>> var $c;
>>>>>
>>>>> function __construct()
>>>>> {
>>>>> $this->c =& A::$a;
>>>>> }
>>>>>
>>>>> }
>>>>>
>>>>>
>>>>> $c = new C;
>>>>> $b = new B;
>>>>> $cee = new C;
>>>>>
>>>>> var_dump($c->c); // [i] prints object(B), but [ii] prints int 3
>>>>> var_dump($cee->c); // [i] prints object(B), and [ii] prints object(B)
>>>>>
>>>>> ?>
>>>>> [/code]
>>>>>
>>>>> Why does $c->c print 'int 3' ?
>>>>>
>>>>> I'm nervous to use "self::$a = $this;" because I don't want to be
>>>>> copying the whole object. However, isn't $this just a reference to the
>>>>> object, so "self::$a = $this;" is just copying the reference and not the
>>>>> actual object, right?
>>>>>
>>>>> Thanks in advance
>>>>
>>>> What do you think the value should be?
>>>>
>>>> A static property is bound to the class and not to an instance of the class.
>>>>
>>>> So, &A::$a is a reference to the static value. If you alter the value,
>>>> it will be altered for a subclasses of A and for any other reference
>>>> to it.
>>>>
>>> I think
>>> var_dump($c->c); would print object(B), but it's printing int 3.
>>>
>>> The reference is *not* being updated. I think this is a bug. What do
>>> you think?
>>>
>>> Thanks
>
> Aha!
>
> $c = new C;
>
> At this stage $c->c will be a reference to the static A::$a = 3.
>
> $b = new B;
>
> Now, as B's constructor calls A's constructor which replaces the
> static A::$a with a reference to the instance $b, the static A::$a
> should now be $b
>
> $cee = new C;
>
> At this stage $cee->c will be a reference to the static A::$a = $b.
>
> But, when var_dump()'d, $c->c !== $cee->c, and I think they should as
> both have been assigned to a reference of a static.
>
> It would seem to be a bug.
>
> I get the same output for V5.0.0 to V5.3.3RC2
>
Thanks for confirming. I reported the bug. I shortened up the test
script quite a bit. Please see: Bug #52332
http://bugs.php.net/bug.php?id=52332
`
From: "Ford, Mike" on
> -----Original Message-----
> From: Daniel Kolbo [mailto:kolb0057(a)umn.edu]
> Sent: 11 July 2010 23:19
>
> Hello PHPers,
>
> I'm having some trouble understanding some PHP behaviour. The
> following
> example script exhibits the behaviour which I cannot understand.

I'm pretty sure that this is *not* a bug. I'll answer your last
question first, and then demonstrate with your code.

> I'm nervous to use "self::$a = $this;" because I don't want to be
> copying the whole object. However, isn't $this just a reference to
> the
> object, so "self::$a = $this;" is just copying the reference and not
> the
> actual object, right?

Not exactly, although everybody seems to refer to it as a reference
for convenience. Most of the time it doesn't matter, but when you
start introducing references to objects it can - it's better to think
of an object variable as holding the object's *handle*
(see http://php.net/anguage.oop5.references.php for more on this), so
it's clear exactly what a reference is referencing.

Now for your code:

> [code]
> <?php
>
> class A
> {
> public static $a = 3;
>
> function __construct()
> {
> //self::$a = $this; //[i]
> self::$a =& $this; //[ii]
> }
> }
>
> class B extends A
> {
> function __construct()
> {
> parent::__construct();
> }
> }
>
> class C {
> var $c;
>
> function __construct()
> {
> $this->c =& A::$a;
> }
>
> }
>
>
> $c = new C;

[i] & [ii] in C::__construct(): $c->c = reference to same value as
A::$a (currently (int)3)
NOTE: because of how references work, A::$a is now also a
reference to (int)3.

> $b = new B;

[i] in A::__construct(): A::$a = handle of object B(1) (also assigned to
global $b)
NOTE: $c->c, as reference to $A::a, now also references handle of
object B(1)

[ii] in A::__construct(): A::$a = reference to handle of object B(1)
NOTE: since we are assigning a new reference to a variable which is
already a reference, ONLY this reference changes -- so $c->c
is still a reference to (int)3...!

> $cee = new C;

Irrelevant -- the damage has already been done!

> var_dump($c->c); // [i] prints object(B), but [ii] prints int 3
> var_dump($cee->c); // [i] prints object(B), and [ii] prints
> object(B)

.... which is correct according to my interpretation.

This has been such a regular confusion, that some time ago I wrote
this test script:

<?php

class test {

public $me;
}

$t = new test;
$t->me = 'Original';

$copy_t = $t;
$ref_t = &$t;

$copy_t = new test;
$copy_t->me = 'Altered Copy';

echo <<<RESULT1
Original: $t->me<br />
Copy: $copy_t->me<br />
Reference: $ref_t->me<br />
RESULT1;

$ref_t = new test;
$ref_t->me = 'Altered Reference';

echo <<<RESULT2
<br />
Original: $t->me<br />
Copy: $copy_t->me<br />
Reference: $ref_t->me<br />
RESULT2;


$s = 'String';

$copy_s = $s;
$ref_s = &$s;

$copy_s = 'String Copy';

echo <<<RESULT3
<br />
Original: $s<br />
Copy: $copy_s<br />
Reference: $ref_s<br />
RESULT3;

$ref_s = 'String Reference';

echo <<<RESULT4
<br />
Original: $s<br />
Copy: $copy_s<br />
Reference: $ref_s<br />
RESULT4;

?>

Which gives this output:

Original: Original
Copy: Altered Copy
Reference: Original

Original: Altered Reference
Copy: Altered Copy
Reference: Altered Reference

Original: String
Copy: String Copy
Reference: String

Original: String Reference
Copy: String Copy
Reference: String Reference

Which demonstrates how exactly the behaviour of objects correlates to
scalars with regards to copying and referencing -- but may not be
exactly what you expect if you think of object variables as always
holding a reference to the object. I would heartily recommend always
to think of an object variable as holding the object's *handle*, and
*not* a reference - this may croggle your brain a bit, but makes it a
Lot clearer what's happening in edge cases like this.

Cheers!

Mike

--
Mike Ford,
Electronic Information Developer, Libraries and Learning Innovation,
Leeds Metropolitan University, C507, Civic Quarter Campus,
Woodhouse Lane, LEEDS,  LS1 3HE,  United Kingdom
Email: m.ford(a)leedsmet.ac.uk
Tel: +44 113 812 4730





To view the terms under which this email is distributed, please go to http://disclaimer.leedsmet.ac.uk/email.htm
From: David Harkness on
Ah, so assigning a reference to a variable already holding a reference
changes that variable's reference only in the same way that unsetting a
reference doesn't unset the other variables referencing the same thing, yes?

$a = 5;
$b = &$a;
print $a;
> 5
unset($b); // does not affect $a
print $a;
> 5

// and according to Mike's previous message
$b = &$a;
$c = 10;
$b = &$c; // does not affect $a
print $a
> 5

That makes a lot of sense. If it didn't work this way there would be no easy
way to untangle references. In the case of

foreach($array as $key => &$value) { ... }

the first value in the array would continuously be overwritten by the next
value.

1. $value gets reference to first array value
2. on each step through the loop, the first array value would be overwritten
by the next value in the loop since $value is forever tied to it by the
initial reference assignment.

That would be a Bad Thing (tm).

Thanks for the clarification, Mike.

David
From: Daniel Kolbo on


David Harkness wrote:
> Ah, so assigning a reference to a variable already holding a reference
> changes that variable's reference only in the same way that unsetting a
> reference doesn't unset the other variables referencing the same thing, yes?
>
> $a = 5;
> $b = &$a;
> print $a;
>> 5
> unset($b); // does not affect $a
> print $a;
>> 5
>
> // and according to Mike's previous message
> $b = &$a;
> $c = 10;
> $b = &$c; // does not affect $a
> print $a
>> 5
>
> That makes a lot of sense. If it didn't work this way there would be no easy
> way to untangle references. In the case of
>
> foreach($array as $key => &$value) { ... }
>
> the first value in the array would continuously be overwritten by the next
> value.
>
> 1. $value gets reference to first array value
> 2. on each step through the loop, the first array value would be overwritten
> by the next value in the loop since $value is forever tied to it by the
> initial reference assignment.
>
> That would be a Bad Thing (tm).
>
> Thanks for the clarification, Mike.
>
> David
>

The big "aha" moment for was when realizing that when assigning a
reference to a variable you only change its reference and not any other
variable that may also have shared the same reference. Yuck that's a
mouth full. This happened when Mike said, " NOTE: since we are
assigning a new reference to a variable which is already a reference,
ONLY this reference changes".

Yes, i agree with you David (on both of your points). Thanks for the
example using the unset. This further clarified/solidified my
understanding.

Now, with this new understanding, I also wish to comment that if i
assign (without reference) $this, i don't have to be too worried about
bloating the memory, b/c i'm only assigning/copying the identifer or
*handle* and not the actual object itself.

In case, someone reads this in the archive the link is:
http://php.net/manual/en/language.oop5.references.php

Mike, thank you a ton.

Regards.
`