473,396 Members | 1,929 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

PHP brain teaser

Here's a little brain teaser distilled from a bug that took me a rather
long time to figure out. The two functions in the example below behave
differently. The difference is easy to spot, of ocurse. The challenge
is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?

Sample code:

<?php

// uncomment the clone operator for PHP 5

function Bobcat(&$obj) {
$clone = /* clone */ $obj;
$obj->attributes['Length'] = 0;
$obj->data = "";
return $clone;
}

function BritneySpear(&$obj) {
$attr =& $obj->attributes;
$clone = /* clone */ $obj;
$obj->attributes['Length'] = 0;
$obj->data = "";
return $clone;
}

$data = "This is a test";

$obj1->attributes = array('Length' => strlen($data));
$obj1->data = $data;
$clone1 = Bobcat($obj1);
print_r($clone1);

$obj2->attributes = array('Length' => strlen($data));
$obj2->data = $data;
$clone2 = BritneySpear($obj2);
print_r($clone2);

?>

Result:

stdClass Object
(
[attributes] => Array
(
[Length] => 14
)

[data] => This is a test
)
stdClass Object
(
[attributes] => Array
(
[Length] => 0
)

[data] => This is a test
)

Jan 13 '06 #1
15 2567
On 2006-01-13, Chung Leong <ch***********@hotmail.com> wrote:
Here's a little brain teaser distilled from a bug that took me a rather
long time to figure out. The two functions in the example below behave
differently. The difference is easy to spot, of ocurse. The challenge
is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?


My guess is the following:
When php initializes a new object (copy/clone) it will notice that in the second
method the reference count to $obj->attributes is 2. Therefore it will assign
new memory for the copied/cloned instance. This explains why we see 0 instead of
14.

--
Met vriendelijke groeten,
Tim Van Wassenhove <http://timvw.madoka.be>
Jan 13 '06 #2
On 2006-01-13, Tim Van Wassenhove <ti***@users.sourceforge.net> wrote:
On 2006-01-13, Chung Leong <ch***********@hotmail.com> wrote:
Here's a little brain teaser distilled from a bug that took me a rather
long time to figure out. The two functions in the example below behave
differently. The difference is easy to spot, of ocurse. The challenge
is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?


My guess is the following:
When php initializes a new object (copy/clone) it will notice that in the second
method the reference count to $obj->attributes is 2. Therefore it will assign
new memory for the copied/cloned instance. This explains why we see 0 instead of
14.


Actually, the new memory is not assigned when the constructor is called, but
when the $obj->attributes['Length'] is set to 0 in the second method (the
copy-on-write behaviour of php).

--
Met vriendelijke groeten,
Tim Van Wassenhove <http://timvw.madoka.be>
Jan 13 '06 #3
On 2006-01-13, Tim Van Wassenhove <ti***@users.sourceforge.net> wrote:
On 2006-01-13, Tim Van Wassenhove <ti***@users.sourceforge.net> wrote:
On 2006-01-13, Chung Leong <ch***********@hotmail.com> wrote:
Here's a little brain teaser distilled from a bug that took me a rather
long time to figure out. The two functions in the example below behave
differently. The difference is easy to spot, of ocurse. The challenge
is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?


My guess is the following:
When php initializes a new object (copy/clone) it will notice that in the second
method the reference count to $obj->attributes is 2. Therefore it will assign
new memory for the copied/cloned instance. This explains why we see 0 instead of
14.


Actually, the new memory is not assigned when the constructor is called, but
when the $obj->attributes['Length'] is set to 0 in the second method (the
copy-on-write behaviour of php).


My brain is tired now as i'm not able to come up with the right words to explane
what is happening.But the reason for this behaviour is explained in
http://derickrethans.nl/files/phparc...es-article.pdf (see figure 6)
--
Met vriendelijke groeten,
Tim Van Wassenhove <http://timvw.madoka.be>
Jan 13 '06 #4
"Chung Leong" <ch***********@hotmail.com> wrote in message
news:11**********************@g43g2000cwa.googlegr oups.com...
Here's a little brain teaser distilled from a bug that took me a rather
long time to figure out. The two functions in the example below behave
differently. The difference is easy to spot, of ocurse. The challenge
is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?
.... $attr =& $obj->attributes;


Since this is the only difference in the two functions, the bug must be
here.

Let me tell you what I'm guessing, but I'm not sure if this is the case
really. $attr is inside the BritneySpear variable scope, and since it's
assigned reference to $obj->attributes which is copied to $clone, the
$clone->attributes is destroyed as the function exits, since $attr is
destroyed.

--
"En ole paha ihminen, mutta omenat ovat elinkeinoni." -Perttu Sirviö
sp**@outolempi.net | Gedoon-S @ IRCnet | rot13(xv***@bhgbyrzcv.arg)
Jan 13 '06 #5

Tim Van Wassenhove wrote:
On 2006-01-13, Tim Van Wassenhove <ti***@users.sourceforge.net> wrote:
On 2006-01-13, Chung Leong <ch***********@hotmail.com> wrote:
Here's a little brain teaser distilled from a bug that took me a rather
long time to figure out. The two functions in the example below behave
differently. The difference is easy to spot, of ocurse. The challenge
is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?


My guess is the following:
When php initializes a new object (copy/clone) it will notice that in the second
method the reference count to $obj->attributes is 2. Therefore it will assign
new memory for the copied/cloned instance. This explains why we see 0 instead of
14.


Actually, the new memory is not assigned when the constructor is called, but
when the $obj->attributes['Length'] is set to 0 in the second method (the
copy-on-write behaviour of php).

--
Met vriendelijke groeten,
Tim Van Wassenhove <http://timvw.madoka.be>


This is the case for the first function. In the second function, the
change to the original object writes through to the cloned object. So
somehow copy-on-write isn't being triggered...

Jan 13 '06 #6
On Thu, 12 Jan 2006 19:01:10 -0800, Chung Leong wrote:
Here's a little brain teaser distilled from a bug that took me a rather
long time to figure out. The two functions in the example below behave
differently. The difference is easy to spot, of ocurse. The challenge
is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?


It doesn't. You are not writing to the clone, you are writing to the
original. You are setting the original data to "", not the clone. Clone
is a bitwise copy, not a reference. If you want it to be something else
then a bitwise copy, you have to define the __clone function. What you
should have written in place of "clone" is $clone =& $obj; Then the
assignment would work and the function would return a reference to
the original object, which is also passed by reference.
--
http://www.mgogala.com

Jan 13 '06 #7
On Fri, 13 Jan 2006 15:12:29 +0000, Mladen Gogala wrote:
On Thu, 12 Jan 2006 19:01:10 -0800, Chung Leong wrote:
Here's a little brain teaser distilled from a bug that took me a rather
long time to figure out. The two functions in the example below behave
differently. The difference is easy to spot, of ocurse. The challenge
is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?


It doesn't. You are not writing to the clone, you are writing to the
original. You are setting the original data to "", not the clone. Clone
is a bitwise copy, not a reference. If you want it to be something else
then a bitwise copy, you have to define the __clone function. What you
should have written in place of "clone" is $clone =& $obj; Then the
assignment would work and the function would return a reference to
the original object, which is also passed by reference.

This produces the results you probably expected:

<?php

function Bobcat(&$obj) {
$clone = clone $obj;
$obj->attributes['Length'] = 0;
$obj->data = "";
return $clone;
}

function BritneySpear(&$obj) {
$attr =& $obj->attributes;
$clone =& $obj;
$obj->attributes['Length'] = 0;
$obj->data = "";
return $clone;
}

$data = "This is a test";

$obj1->attributes = array('Length' => strlen($data));
$obj1->data = $data;
$clone1 = Bobcat($obj1);
print_r($clone1);

$obj2->attributes = array('Length' => strlen($data));
$obj2->data = $data;
$clone2 = BritneySpear($obj2);
print_r($clone2);

?>

$ php ttt
stdClass Object
(
[attributes] => Array
(
[Length] => 14
)

[data] => This is a test
)
stdClass Object
(
[attributes] => Array
(
[Length] => 0
)

[data] =>
)
$

--
http://www.mgogala.com

Jan 13 '06 #8
On 2006-01-13, Mladen Gogala <go****@sbcglobal.net> wrote:
On Fri, 13 Jan 2006 15:12:29 +0000, Mladen Gogala wrote:
On Thu, 12 Jan 2006 19:01:10 -0800, Chung Leong wrote:
Here's a little brain teaser distilled from a bug that took me a rather
long time to figure out. The two functions in the example below behave
differently. The difference is easy to spot, of ocurse. The challenge
is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?
It doesn't. You are not writing to the clone, you are writing to the
original. You are setting the original data to "", not the clone. Clone
is a bitwise copy, not a reference. If you want it to be something else
then a bitwise copy, you have to define the __clone function. What you
should have written in place of "clone" is $clone =& $obj; Then the
assignment would work and the function would return a reference to
the original object, which is also passed by reference.


Changing the statement below to: $attr = $obj->attributes would already make
the curiosity disappear.
$attr =& $obj->attributes;


I still think it's because after this statement the container that holds the
$obj->attributes will now have is_ref = 1 which seems to lead to a copy of this
container when $obj is assigned to $clone ($clone = $obj). For some reason php
doesn't seem to make a deep-copy of the array but simply makes an array with
references to the original array elements.

--
Met vriendelijke groeten,
Tim Van Wassenhove <http://timvw.madoka.be>
Jan 13 '06 #9
Tim Van Wassenhove wrote:

I still think it's because after this statement the container that holds the
$obj->attributes will now have is_ref = 1 which seems to lead to a copy of this
container when $obj is assigned to $clone ($clone = $obj). For some reason php
doesn't seem to make a deep-copy of the array but simply makes an array with
references to the original array elements.


That's it. You have got to the root of the problem. During cloning, PHP
does not create a copy of an object property if it is a reference. From
the manual: "Any properties that are references to other variables,
will remain references." The hard part was, of course, recognizing that
$obj->attributes is a reference. It's extremely unintuitive that the
line

$attr =& $obj->attributes;

not only makes $attr a reference, but turns $obj->attributes into a
reference too. Programmers usually don't expect side-effects on the
right side of an assignment operator.

Jan 14 '06 #10

"Chung Leong" <ch***********@hotmail.com> wrote in message
news:11**********************@g44g2000cwa.googlegr oups.com...
Tim Van Wassenhove wrote:

I still think it's because after this statement the container that holds
the
$obj->attributes will now have is_ref = 1 which seems to lead to a copy
of this
container when $obj is assigned to $clone ($clone = $obj). For some
reason php
doesn't seem to make a deep-copy of the array but simply makes an array
with
references to the original array elements.


That's it. You have got to the root of the problem. During cloning, PHP
does not create a copy of an object property if it is a reference. From
the manual: "Any properties that are references to other variables,
will remain references." The hard part was, of course, recognizing that
$obj->attributes is a reference. It's extremely unintuitive that the
line

$attr =& $obj->attributes;

not only makes $attr a reference, but turns $obj->attributes into a
reference too. Programmers usually don't expect side-effects on the
right side of an assignment operator.


This doesn't sound technically correct. an operator (such as =& ) in a
parser is generally never allowed to modify the RHS (right-hand-side). This
jives with the article he mentioned.
The correct way to read $b =& $a is $b is assigned a reference to $a.
according to the diagrams shown, $a and $b are both C pointers (to data) in
the first place.
in C (under the hood), the pointer assignment is simply b=a.
PHP's = is probably a simple C call to malloc() the LHS pointer to allocate
some memory and then memcpy() to copy the RHS data over that newly allocated
memory.
Ever wonder why a plain var like $a can be assigned NULL? because possibly
a C pointer can be assigned NULL. You learn this stuff in ANSI C classes
(which is what PHP was likely written in).
This tells me that $a=5; probably means $a is a pointer to an int. PHP's ->
is a dereferencing an extra pointer.
You can think of then as references before they were ever assigned.
references are simply pointers. variable names are simply pointers.
Technically, the reference (pointer) itself should get copied in the process
of cloning if I am correct. I guess I would have to look at that weird code
again. did you find a bug in PHP there?
That article mentions at the end that it's possible to corrupt memory with
references (that's definitely true with C pointers... been there, done
that - ever heard of GPF?).
Jan 15 '06 #11
Jim Michaels wrote:
This doesn't sound technically correct. an operator (such as =& ) in a
parser is generally never allowed to modify the RHS (right-hand-side). This
jives with the article he mentioned.


In a way the term "reference" is unfortunate. In C++ being a reference
is a variable's intrinsic, whereas in PHP a variable "is a reference"
if the data it points to is shared--an external condition.

One could argue that there is no changes on the right-hand-side of an
assignment if = and & are treated as separate operators and any
side-effect is caused by the latter. You can put white spaces between
the two after all. On the other hand, & is really just a modifier and
we have just one operator even with white spaces. In any event, a
couple changes can occur on the right-hand-side of a =&:

1. $b =& $a makes both $b and $a references. This is actually perfectly
logical: since $a and $b are point to the same data, $a can be used to
modify what's in $b--hence it is a reference. It's only funky from the
C/C++ perspective, as the type of $a has suddenly changed.

2. Autovivication is triggered.

Example:

<?php
error_reporting(E_ALL);
$b = &$a['Goodbye']['cruel']['world']->letter;
print_r($a);
?>

Result:

Array
(
[Goodbye] => Array
(
[cruel] => Array
(
[world] => stdClass Object
(
[letter] =>
)

)

)

)

Jan 15 '06 #12
I would agree that & is a modifier. if $a were truly converted from real
data into a reference the data would have been destroyed by modifying the
RHS. That's illegal in a language parser. I was speaking more about the
internal representation and the way the parser probably works. & just says
"give me the address of $a". Maybe I am misunderstanding what you are trying
to say.
"Chung Leong" <ch***********@hotmail.com> wrote in message
news:11**********************@g14g2000cwa.googlegr oups.com...
Jim Michaels wrote:
This doesn't sound technically correct. an operator (such as =& ) in a
parser is generally never allowed to modify the RHS (right-hand-side).
This
jives with the article he mentioned.


In a way the term "reference" is unfortunate. In C++ being a reference
is a variable's intrinsic, whereas in PHP a variable "is a reference"
if the data it points to is shared--an external condition.

One could argue that there is no changes on the right-hand-side of an
assignment if = and & are treated as separate operators and any
side-effect is caused by the latter. You can put white spaces between
the two after all. On the other hand, & is really just a modifier and
we have just one operator even with white spaces. In any event, a
couple changes can occur on the right-hand-side of a =&:

1. $b =& $a makes both $b and $a references. This is actually perfectly
logical: since $a and $b are point to the same data, $a can be used to
modify what's in $b--hence it is a reference. It's only funky from the
C/C++ perspective, as the type of $a has suddenly changed.

2. Autovivication is triggered.

Example:

<?php
error_reporting(E_ALL);
$b = &$a['Goodbye']['cruel']['world']->letter;
print_r($a);
?>

Result:

Array
(
[Goodbye] => Array
(
[cruel] => Array
(
[world] => stdClass Object
(
[letter] =>
)

)

)

)

Jan 15 '06 #13
I don't know what you're driving at. I'm telling you that's how PHP
works. Your speculations are simply incorrect.

Jan 16 '06 #14
Chung Leong wrote:

In a way the term "reference" is unfortunate. In C++ being a reference
is a variable's intrinsic, whereas in PHP a variable "is a reference"
if the data it points to is shared--an external condition.

Actually, not. In C++ a reference is a different type of variable.

int i = 4; // the real thing
int & j = i; // a reference to i.

The second statement does NOT change i in any way. the same is true in PHP:

$i = 4;
$j = & $i;

$i is an value; $j is a reference to the value.

The big difference is - $i has independent storage assigned to it to
contain the data. $j does not contain any data - it only "refers" to $i.
One could argue that there is no changes on the right-hand-side of an
assignment if = and & are treated as separate operators and any
side-effect is caused by the latter. You can put white spaces between
the two after all. On the other hand, & is really just a modifier and
we have just one operator even with white spaces. In any event, a
couple changes can occur on the right-hand-side of a =&:

No, there is no change in $i when you assign a reference to it.
1. $b =& $a makes both $b and $a references. This is actually perfectly
logical: since $a and $b are point to the same data, $a can be used to
modify what's in $b--hence it is a reference. It's only funky from the
C/C++ perspective, as the type of $a has suddenly changed.


Nope, the type of $a has NOT changed.

--
==================
Remove the "x" from my email address
Jerry Stuckle
JDS Computer Training Corp.
js*******@attglobal.net
==================
Jan 16 '06 #15
Jerry Stuckle wrote:
Actually, not. In C++ a reference is a different type of variable.

int i = 4; // the real thing
int & j = i; // a reference to i.

The second statement does NOT change i in any way. the same is true in PHP:

$i = 4;
$j = & $i;

$i is an value; $j is a reference to the value.
That's a common misconception about references in PHP. I suggest
reading the article that Tim referred to earlier.
No, there is no change in $i when you assign a reference to it.


I am afraid you're arguing with reality here. To illustrate...

<?php

$obj->i = 5;
debug_zval_dump($obj);

$obj->j =& $obj->i;
debug_zval_dump($obj);

?>

Result:

object(stdClass)(1) refcount(2){
["i"]=>
long(5) refcount(1)
}
object(stdClass)(2) refcount(2){
["i"]=>
&long(5) refcount(2)
["j"]=>
&long(5) refcount(2)
}

Jan 16 '06 #16

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: brendan | last post by:
here's a brain teaser for someone with more skills or at least more lateral thinking capability than me - done my nut over this one... have written a list manager in PHP which a) posts out new...
7
by: robert | last post by:
running 8.1.7 server, 8.1.6 client. i *thought* inner join should not return nulls, but not only that, but i get way more rows than i'm expecting. assume: order table: order_number
7
by: One Handed Man \( OHM - Terry Burns \) | last post by:
I've been battling with this stupid problem for hours now. WebApp: Trying to do a simple transformation using XSLT to a Web Page, but it just failes without an error message ( In other words,...
3
by: RC | last post by:
Dear Dudes, I post this in multiple groups for opening brain storm. Sometime I need to query the data from database server then display them into user's browser in HTML <table>. But if the...
7
by: Mark A | last post by:
If server 01 running HADR in the primary role crashes, and the DBA does a HADR takeover by force on the 02 server to switch roles, then the 02 server is now the primary. What happens when the...
3
by: datagal | last post by:
I have a requirement (motivated by a SOX thing) that is just giving me fits. I know it should be easy and I'm probably overthinking it, but I just can seem to find the best way to get where I need...
3
by: Joachim Klassen | last post by:
Hi all, if I accidentally use a TAKEOVER command with BY FORCE clause while primary and standby are in peer state I'll end up with two primary's (at least with FP10 and Windows). Is this works ...
2
by: oorga.power | last post by:
Our 90 % percent work based on our mind. all the successful person of this world won from mind. C Langugae is great language, it needs logics. Logics can only give your logical part of brain,...
2
RedSon
by: RedSon | last post by:
Given a directed graph of k-nodes such that the last node creates a cycle with any other node determine which node the last node's edge points to using the minimum amount of resources and without...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.