+add a note
User Contributed Notes 34 notes
Dave at SymmetricDesigns dot com ¶
16 years ago
Another example of something to watch out for when using references with arrays. It seems that even an usused reference to an array cell modifies the *source* of the reference. Strange behavior for an assignment statement (is this why I've seen it written as an =& operator? - although this doesn't happen with regular variables).
<?php
$array1 = array(1,2);
$x = &$array1[1]; // Unused reference
$array2 = $array1; // reference now also applies to $array2 !
$array2[1]=22; // (changing [0] will not affect $array1)
print_r($array1);
?>
Produces:
Array
(
[0] => 1
[1] => 22 // var_dump() will show the & here
)
I fixed my bug by rewriting the code without references, but it can also be fixed with the unset() function:
<?php
$array1 = array(1,2);
$x = &$array1[1];
$array2 = $array1;
unset($x); // Array copy is now unaffected by above reference
$array2[1]=22;
print_r($array1);
?>
Produces:
Array
(
[0] => 1
[1] => 2
)
Carlos ¶
19 years ago
in the example below, you would get the same result if you change the function to something like:
function test_ref(&$arr) {
$time = time();
$size = sizeof($arr); // <--- this makes difference...
for($n=0; $n<$size; $n++) {
$x = 1;
}
echo "<br />The function using a reference took ".(time() - $time)." s";
}
ivan at mailinator dot com ¶
16 years ago
A little gotcha (be careful with references!):
<?php
$arr = array('a'=>'first', 'b'=>'second', 'c'=>'third');
foreach ($arr as &$a); // do nothing. maybe?
foreach ($arr as $a); // do nothing. maybe?
print_r($arr);
?>
Output:
Array
(
[a] => first
[b] => second
[c] => second
)
Add 'unset($a)' between the foreachs to obtain the 'correct' output:
Array
(
[a] => first
[b] => second
[c] => third
)
hkmaly at bigfoot dot com ¶
20 years ago
It seems like PHP has problems with references, like that it can't work properly with circular references or free properly structure with more references. See http://bugs.php.net/?id=30053.
I have big problem with this and I hope someone from PHP add proper warning with explanation IN manual, if they can't fix it.
alexander at gamerev dot org ¶
10 years ago
Simply put, here's an example of what referencing IS:
<?php
$foo = 5;
$bar = &$foo;
$bar++;
echo $foo;
?>
The above example will output the value 6, because $bar references the value of $foo, therefore, when changing $bar's value, you also change $foo's value too.
gnuffo1 at gmail dot com ¶
14 years ago
If you want to know the reference count of a particular variable, then here's a function that makes use of debug_zval_dump() to do so:
<?php
function refcount($var)
{
ob_start();
debug_zval_dump($var);
$dump = ob_get_clean();
$matches = array();
preg_match('/refcount\(([0-9]+)/', $dump, $matches);
$count = $matches[1];
//3 references are added, including when calling debug_zval_dump()
return $count - 3;
}
?>
debug_zval_dump() is a confusing function, as explained in its documentation, as among other things, it adds a reference count when being called as there is a reference within the function. refcount() takes account of these extra references by subtracting them for the return value.
It's also even more confusing when dealing with variables that have been assigned by reference (=&), either on the right or left side of the assignment, so for that reason, the above function doesn't really work for those sorts of variables. I'd use it more on object instances.
However, even taking into account that passing a variable to a function adds one to the reference count; which should mean that calling refcount() adds one, and then calling debug_zval_dump() adds another, refcount() seems to have aquired another reference from somewhere; hence subtracting 3 instead of 2 in the return line. Not quite sure where that comes from.
I've only tested this on 5.3; due to the nature of debug_zval_dump(), the results may be completely different on other versions.
php at REMOVEMEkennel17 dot co dot uk ¶
19 years ago
I found a very useful summary of how references work in PHP4 (and some of the common pitfalls) in this article: http://www.obdev.at/developers/articles/00002.html
It deals with some subtle situations and I recommend it to anyone having difficulty with their references.
nathan ¶
20 years ago
On the post that says php4 automagically makes references, this appears to *not* apply to objects:
http://www.php.net/manual/en/language.references.whatdo.php
"Note: Not using the & operator causes a copy of the object to be made. If you use $this in the class it will operate on the current instance of the class. The assignment without & will copy the instance (i.e. the object) and $this will operate on the copy, which is not always what is desired. Usually you want to have a single instance to work with, due to performance and memory consumption issues."
midir ¶
15 years ago
Here is a good magazine article (PDF format) that explains the internals of PHP's reference mechanism in detail: http://derickrethans.nl/files/phparch-php-variables-article.pdf
It should explain some of the odd behavior PHP sometimes seems to exhibit, as well as why you can't create "references to references" (unlike in C++), and why you should never attempt to use references to speed up passing of large strings or arrays (it will make no difference, or it will slow things down).
It was written for PHP 4 but it still applies. The only difference is in how PHP 5 handles objects: passing object variables by value only copies an internal pointer to the object. Objects in PHP 5 are only ever duplicated if you explicitly use the clone keyword.
mpapec ¶
16 years ago
It's strange that function definition AND call to the same function must have "&" before them.
$arr = array();
$ref =& oras($arr['blah'], array());
$ref []= "via ref";
print_r($arr);
/* result
Array
(
[blah] => Array
(
[0] => via ref
)
)
*/
// perl like ||=
function &oras (&$v, $new) {
$v or $v = $new;
return $v;
}
iryoku at terra dot es ¶
20 years ago
You should have in mind that php4 keep assigned variables "automagically" referenced until they are overwritten. So the variable copy is not executed on assignment, but on modification. Say you have this:
$var1 = 5;
$var2 = $var1; // In this point these two variables share the same memory location
$var1 = 3; // Here $var1 and $var2 have they own memory locations with values 3 and 5 respectively
Don't use references in function parameters to speed up aplications, because this is automatically done. I think that this should be in the manual, because it can lead to confusion.
More about this here:
http://www.zend.com/zend/art/ref-count.php
dallgoot ¶
6 years ago
After some headaches, here is a function to check if, between 2 variables $a,$b check if one is a reference to the other.
It means they "point" to the same value.
Tested on :
PHP 7.2.2 (cli) (built: Jan 31 2018 19:31:17) ( ZTS MSVC15 (Visual C++ 2017) x64 )
Hope that helps...
<?php
function are_references(&$a, &$b){
$mem = $a; //memorize
$a = uniqid ("REFERENCE???", true ); //change $a
$same = $a === $b; //compare
$a = $mem; //restore $a
return $same;
}
echo "***distinct vars AND distinct values\n";
$a = "toto";
$b = "tata";
var_dump($a, $b, are_references($a, $b));
echo "verify original values: $a, $b\n";
echo "***distinct vars BUT SAME values\n";
$a = "toto";
$b = "toto";
var_dump($a, $b, are_references($a, $b));
echo "verify original values: $a, $b\n";
echo '*** $b is a reference of $a'."\n";
$a = "titi";
$b = &$a;
var_dump($a, $b, are_references($a, $b));
echo "verify original values: $a, $b\n";
echo '*** $a is a reference of $b'."\n";
$b = "titi";
$a = &$b;
var_dump($a, $b, are_references($a, $b));
echo "verify original values: $a, $b\n";
?>
Result:
***distinct vars AND distinct values
string(4) "toto"
string(4) "tata"
bool(false)
verify original values: toto, tata
***distinct vars BUT SAME values
string(4) "toto"
string(4) "toto"
bool(false)
verify original values: toto, toto
*** $b is a reference of $a
string(4) "titi"
string(4) "titi"
bool(true)
verify original values: titi, titi
*** $a is a reference of $b
string(4) "titi"
string(4) "titi"
bool(true)
verify original values: titi, titi
marco at greenlightsolutions dot nl ¶
17 years ago
I ran into a bit of a problem recently, with an array copy resulting in a reference copy of one of the elements instead of a clone. Sample code:
<?php
$a=array(1 => "A");
$b=&$a[1];
$c=$a; // should be a deep cloning
$c[1]="C";
var_dump($a[1]); // yields 'C' instead of 'A'
?>
After some searching, I found that it was a known bug which would be too costly to fix (see http://bugs.php.net/bug.php?id=20993). There was supposed to be some documentation on this behaviour on this page:
"Due to peculiarities of the internal workings of PHP, if a reference is made to a single element of an array and then the array is copied, whether by assignment or when passed by value in a function call, the reference is copied as part of the array. This means that changes to any such elements in either array will be duplicated in the other array (and in the other references), even if the arrays have different scopes (e.g. one is an argument inside a function and the other is global)! Elements that did not have references at the time of the copy, as well as references assigned to those other elements after the copy of the array, will behave normally (i.e. independent of the other array)."
However, this paragraph appears to have been removed from this page at some point, presumably because it was a bit obscure. The comments section seem to be a proper place for this, though.
dnhuff at acm dot org ¶
16 years ago
This is discussed before (below) but bears repeating:
$a = null; ($a =& null; does not parse) is NOT the same as unset($a);
$a = null; replaces the value at the destination of $a with the null value;
If you chose to use a convention like $NULL = NULL;
THEN, you could say $a =& $NULL to break any previous reference assignment to $a (setting it of course to $NULL), which could still get you into trouble if you forgot and then said $a = '5'. Now $NULL would be '5'.
Moral: use unset when it is called for.
Someone ¶
9 years ago
Another example of something to watch out for when using references with arrays. It seems that even an usused reference to an array cell modifies the *source* of the reference. Strange behavior for an assignment statement (is this why I've seen it written as an =& operator? - although this doesn't happen with regular variables).
<?php
$array1 = array(1,2);
$x = &$array1[1]; // Unused reference
$array2 = $array1; // reference now also applies to $array2 !
$array2[1]=22; // (changing [0] will not affect $array1)
print_r($array1);
?>
Produces:
Array
(
[0] => 1
[1] => 22 // var_dump() will show the & here
)
//above was Noted By Dave at SymmetricDesign dot com//
//and below is my opinion to this simple problem. //
This is an normal referencing problem.
when you gain an reference to a memory at some variable.
this variable, means "memory itself". (in above example, this would be -> $x = &$array1[1]; // Unused reference)
and you've copied original one($array1) to another one($array2).
and the copy means "paste everything on itself". including references or pointers, etcs. so, when you copied $array1 to $array2, this $array2 has same referencers that original $array1 has. meaning that $x = &$array1[1] = &$array2[1];
and again i said above. this reference means "memory itself".
when you choose to inserting some values to $array2[1],
$x; reference is affected by $array2[1]'s value. because, in allocated memory, $array2[1] is a copy of $array1[1]. this means that $array2[1] = $array1[1], also means &$array2[1] = &$array1[1] as said above. this causes memory's value reallocation on $array1[1]. at this moment. the problem of this topic is cleared by '$x', the memory itself. and this problem was solved by unsetting the '$x'. unsetting this reference triggers memory reallocation of $array2[1]. this closes the reference link between the copied one($array1, which is the original) and copy($array2). this is where that bug(clearly, it's not a bug. it's just a missunderstanding) has triggered by. closing reference link makes two array object to be separated on memory. and this work was done through the unset() function. this topic was posted 7 years ago, but i just want to clarify that it's not a bug.
if there's some problems in my notes, plz, note that on above.
sneskid at hotmail dot com ¶
12 years ago
There is no built in method (yet) to check if two variables are references to the same piece of data, but you can do a "reference sniff" test. This is rarely needed, but can be very useful. The function bellow is a slightly modified version of this technique I saw in a forum regarding this comparison limitation.
<?php
function is_ref_to(&$a, &$b)
{
$t = $a;
if($r=($b===($a=1))){ $r = ($b===($a=0)); }
$a = $t;
return $r;
}
$varA = 1;
$varB = $varA;
$varC =&$varA;
var_dump( is_ref_to($varA, $varB) ); // bool(false)
var_dump( is_ref_to($varA, $varC) ); // bool(true)
?>
The test above uses a two step process to be 100% generic.
But if you are sure the variables being tested will not be a certain value, example null, then use that value to allow a one step check.
<?php
function is_ref_to_1step(&$a, &$b)
{
$t = $a;
$r=($b===($a=null));
$a = $t;
return $r;
}
?>
sneskid at hotmail dot com ¶
17 years ago
(v5.1.4)
One cool thing about var_dump is it shows which variables are references (when dumping arrays), symbolized by '∫' for int/null, and by '&' for boolean/double/string/array/object. I don't know why the difference in symmmmbolism.
After playing around I found a better way to implement detaching (twas by accident). var_dump can show what's going on.
<?php
function &detach($v=null){return $v;}
$A=array('x' => 123, 'y' => 321);
$A['x'] = &$A['x'];
var_dump($A);
/* x became it's own reference...
array(2) {
["x"]=> ∫(123)
["y"]=> int(321)
}*/
$A['y']=&$A['x'];
var_dump($A);
/* now both are references
array(2) {
["x"]=> ∫(123)
["y"]=> ∫(123)
}*/
$z = 'hi';
$A['y']=&detach(&$z);
var_dump($A);
/* x is still a reference, y and z share
array(2) {
["x"]=> ∫(123)
["y"]=> &string(2) "hi"
}*/
$A['x'] = $A['x'];
$A['y']=&detach();
var_dump($A,$z);
/* x returned to normal, y is on its own, z is still "hi"
array(2) {
["x"]=> int(123)
["y"]=> NULL
}*/
?>
For detach to work you need to use '&' in the function declaration, and every time you call it.
Use this when you know a variable is a reference, and you want to assign a new value without effecting other vars referencing that piece of memory. You can initialize it with a new constant value, or variable, or new reference all in once step.
sneskid at hotmail dot com ¶
18 years ago
in addition to what 'jw at jwscripts dot com' wrote about unset; it can also be used to "detach" the variable alias so that it may work on a unique piece of memory again.
here's an example
<?php
define('NL', "\r\n");
$v1 = 'shared';
$v2 = &$v1;
$v3 = &$v2;
$v4 = &$v3;
echo 'before:'.NL;
echo 'v1=' . $v1 . NL;
echo 'v2=' . $v2 . NL;
echo 'v3=' . $v3 . NL;
echo 'v4=' . $v4 . NL;
// detach messy
$detach = $v1;
unset($v1);
$v1 = $detach;
// detach pretty, but slower
eval(detach('$v2'));
$v1 .= '?';
$v2 .= ' no more';
$v3 .= ' sti';
$v4 .= 'll';
echo NL.'after:'.NL;
echo 'v1=' . $v1 . NL;
echo 'v2=' . $v2 . NL;
echo 'v3=' . $v3 . NL;
echo 'v4=' . $v4 . NL;
function detach($v) {
$e = '$detach = ' . $v . ';';
$e .= 'unset('.$v.');';
$e .= $v . ' = $detach;';
return $e;
}
?>
output {
before:
v1=shared
v2=shared
v3=shared
v4=shared
after:
v1=shared?
v2=shared no more
v3=shared still
v4=shared still
}
http://www.obdev.at/developers/articles/00002.html says there's no such thing as an "object reference" in PHP, but with detaching it becomes possible.
Hopefully detach, or something like it, will become a language construct in the future.
jw at jwscripts dot com ¶
20 years ago
Re-using variables which where references before, without unsetting them first, leads to unexpected behaviour.
The following code:
<?php
$numbers = array();
for ($i = 1; $i < 4; $i++) {
$numbers[] = null;
$num = count($numbers);
$index =& $numbers[$num ? $num - 1 : $num];
$index = $i;
}
foreach ($numbers as $index) {
print "$index\n";
}
?>
Does not produce:
1
2
3
But instead:
1
2
2
Applying unset($index) before re-using the variable fixes this and the expected list will be produced:
1
2
3
gunter dot sammet at gmail dot com ¶
18 years ago
I tried to create an array with n depth using a recursive function passing array references around. So far I haven't had much luck and I couldn't find anything on the web. So I ended up using eval() and it seems to work well:
<?php
foreach(array_keys($this->quantity_array) AS $key){
if($this->quantity_array[$key] > 0){
$combinations = explode('-', $key);
$eval_string = '$eval_array';
foreach(array_keys($combinations) AS $key2){
$option_key_value = explode('_', $combinations[$key2]);
$eval_string .= '['.$option_key_value[0].']['.$option_key_value[1].']';
}
$eval_string .= ' = '.$this->quantity_array[$key].';';
eval($eval_string);
}
}
?>
This produces an n dimensional array that will be available in the $eval_array variable. Hope it helps somebody!
Ed ¶
18 years ago
Responding to Slava Kudinov. The only reason why your script takes longer when you pass by reference is that you do not at all modify the array that your passing to your functions. If you do that the diffrences in execution time will be a lot smaller. In fact passing by reference will be faster if just by a little bit.
shooo dot xz at gmail dot com ¶
11 years ago
Hi, i've worked a abit on reference stuff.
Here is what i've noticed.
The problem: Sort mysql result through columns
$rewrite_self = gt::recombine(array('lang', 'id'), 'value_column', $sql_result);
public static function recombine($keys, $value, &$arr) {
$ref = array();
$main = &$ref;
foreach($arr as $data) {
foreach($keys as $key) {
if (!is_array($ref[$data[$key]])) $ref[$data[$key]] = array();
$ref = &$ref[$data[$key]];
}
$ref = $data[$value];
$ref = &$main;
}
return $main;
}
array(2) {
["pl"]=>
array(2) {
[2]=>
string(4) "value_column_str"
[3]=>
string(4) "value_column_str2"
}
["en"]=>
array(1) {
[13]=>
string(7) "value_column_str3"
}
}
Enjoy!
Francis dot a at gmx dot net ¶
19 years ago
I don't know if this is a bug (I'm using PHP 5.01) but you should be careful when using references on arrays.
I had a for-loop that was incredibly slow and it took me some time to find out that most of the time was wasted with the function sizeof() at every loop, and even more time I spent finding out that this problem it must be somehow related to the fact, that I used a reference of the array. Take a look at the following example:
function test_ref(&$arr) {
$time = time();
for($n=0; $n<sizeof($arr); $n++) {
$x = 1;
}
echo "<br />The function using a reference took ".(time() - $time)." s";
}
function test_val($arr) {
$time = time();
for($n=0; $n<sizeof($arr); $n++) {
$x = 1;
}
echo "<br />The funktion using a value took: ".(time() - $time)." s";
}
// fill array
for($n=0; $n<2000; $n++) {
$ar[] = "test".$n;
}
test_ref($ar);
test_val($ar);
echo "<br />Done";
When I tested it, the first function was done after 9 seconds, while the second (although the array must be copied) was done in not even one.
The difference is inproportional smaller when the array size is reduced:
When using 1000 loops the first function was running for 1 second, when using 4000 it wasn't even done after 30 Seconds.