Skip to main content.

Monday, August 16, 2004

In order to understand some parts of the Nucleus core code, you need to know what references are, how they work, and how to use them. This article tries to explain the basics of references. If you're familiar with programming languages like C or C++, this will look trivial. But remember: PHP references are NOT the same as pointers in C, they're more like symbol table aliasses.

Knowing how references work will help you understand the Nucleus MANAGER class and the Nucleus Plugin API.

Variables & Memory

Conceptually, an assignment to a variable will associate the variable with some location in memory that contains the data from the assignment.

$a = 123

After this code, the $a variable will point to some memory location containing a value of 123. Nothing unexpected so far.

$a = 123

References

Now, let's consider the following piece of code:

$b = $a
$c =& $a

What will happen?

It turns out that we've created two new variables, called $b and $c. The first one ($b) points to a new memory location, containing the same value as $a did. The value has been copied. $c on the other hand, refers to the same memory location as $a did, because of the =& operator. No data has been copied.

$a = $c = 123; $b = 123

Some more code:

$a++; $b++; $c++;

Quite obvious, the values of each of the variables is increased by one. But since $a and $c point to the same code, the final value for these variables will be 125.

$a = $c = 125; $b = 124

Function arguments

Let's add some more code. We'll be adding two functions and will be calling them.

function ref(&$x) { $x = $x / 2; }
function nonref($x) { $x = $x / 2; }

ref($b);
nonref($c);

You'll notice that the ref function has & before its parameter name. It indicates that the parameter should be passed by reference rather than by value. It also means that if we call this method on $b, the resulting value in $b will be 62.

The nonref method, on the other hand, gets its parameters by value. The calculation inside the function is thus done on a temporary copy and therefore will not affect $c or $a.

$a = $c = 125; $b = 62

Arrays!

Let's throw in some arrays. Arrays can also contain references, complicating things once more.

$r = array(&$a, &$b, $c);

So, what does the memory look like now? The array contains three values, two of which are references to $a and $b.

$a = $b = $r[0] = 125; $b = $r[1] = 62; $r[2] = 125

You'll probably guess what the following code has as result:

$r[0]++; $r[1]++; $r[2]++;
$a = $b = $r[0] = 126; $b = $r[1] = 63; $r[2] = 126

Now for a tricky one: let's pass the array as a whole to some functions. One of them takes an array by reference, the other one takes it by values. First, let's define our functions:

function a_ref(&$x) { $x[0]++; $x[1]++; $x[2]++; }
function a_nonref($x) { $x[0]++; $x[1]++; $x[2]++; }

Now, let's call a_ref($r) first. As you expect, the result will be as follows:

$a = $b = $r[0] = 127; $b = $r[1] = 64; $r[2] = 127

Maybe a little unexpected, but when calling the method with copy semantics (a_nonref($r)), we see that the values still change (except for $r[2])!

$a = $b = $r[0] = 128; $b = $r[1] = 65; $r[2] = 127

The reason for this, is that even though the array $r is copied to a temporary $x variable before executing the code in the function, taking a copy of a reference yields the same reference. So while executing a_nonref, $x contains two references (to $a and $b), plus a copy of the value 127. The fact that the 127 value is copied is the reason why $r[2] hasn't changed to 128 here.

The danger of foreach

One important thing to know is that a foreach loop will take a copy for each iteration. This means that the following code will not alter $r

foreach ($r as $foo) 
{
   $foo++;
}

Instead, you should use the following code:

foreach (array_keys($r) as $idx)
{
   $foo =& $r[$idx];
   ...
}

Be careful not to use foreach ($r as &$foo), since this only works in PHP5+. It looks cleaner, though :)

Call-time pass-by-reference

In earlier versions of PHP it was possible to do something like:

my_func(&$foo)

function my_func($x) {}

This behavior has long been deprecated, and should never be used. In php.ini, there's an option allow_call_time_pass_reference which is nowadays generally set to Off.

So, why would references be useful?

The samples above (creating references to simple integers) aren't really useful in daily life. References are mostly used in Nucleus to avoid copying of entire objects, to save memory usage and to allow plugins to edit certain data while handling events.

What is the effect on performance?

Quoting part of the phpPatterns() - PHP and Variable References article:

On the one hand, passing a variable by reference costs more in terms or performance than passing a copy, as PHP has more work to do locating the referenced value of the variable.

On the other hand, every copy of a variable we make uses up more of the memory available to PHP, so we also need to be careful here.

In general it's worth being aware of the impact of both on our code, but we should also remember that the method we use should be determined by the design of the application itself and the specific "problem" we are dealing with, rather than applying arbitary performance optimisation without considering what we actually need.

Further reading

Hope you enjoyed the show!

Comments

So it's like pointers in C, cool article.

Posted by hcgtv at Monday, August 16, 2004 22:09:10

Really interesting article! Especially the part about the arrays :-)
I have some knowledge about pointers from my C course but while working at the core i had some problems with references in arrays, now it is all clear :-)
THX!

Posted by TeRanEX at Monday, August 16, 2004 23:57:22

Thanks for explaining the memory and performance part of it. That was exactly what I was interested in. Nice to know that PHP5 allows for references in foreach (which really is something that comes in very handy).

Posted by Thomas Gottschall at Wednesday, July 06, 2005 08:57:34

Add Comment

This item is closed, it's not possible to add new comments to it or to vote on it