In simple words, references are aliases.
A variable in PHP is stored in two pieces: the name and the value. The name points to the value.
$x = 2;
$y = $x;
$z = &$x;
When $x = 2;
is executed, the name x
is stored in the symbol table of the current scope and the value 2
is stored in a zval
(don't ask, it's the internal name of a data structure that stores a value in PHP).
When $y = $x;
is executed, the name y
is stored in the symbol table of the current scope and the value of $x
(2
) is copied into a new zval
structure.
When $z = &$x;
is executed, the name z
is stored in the symbol table of the current scope but a new zval
is not created. Instead, z
is set to point to the same zval
as x
.
The memory used by the variables $x
, $y
and $z
looks like this:
+---------+ +---------+
| x | -------------> | 2 |
+---------+ +---------+
^
+---------+ | +---------+
| y | -------------------------> | 2 |
+---------+ | +---------+
|
+---------+ |
| z | --------------------+
+---------+
When a value is passed by reference to a function or a function returns a reference the same things happen, only the names are stored in different symbol tables (remark the "current scope" in the explanation above).
Let' see this code:
function f(& $z) {
$y = $z;
$z = $z + 2;
}
$x = 2;
f($x);
After $x = 2;
the memory looks like this:
+---------+ +---------+
| x | -------------> | 2 |
+---------+ +---------+
During the execution of function f()
, the memory looks like this:
+===== global ====+
| +---------+ | +---------+
| | x | -------------> | 4 |
| +---------+ | +---------+
+=================+ ^
|
+====== f() ======+ |
| +---------+ | | +---------+
| | y | -------------------------> | 2 |
| +---------+ | | +---------+
| | |
| +---------+ | |
| | z | --------------------+
| +---------+ |
+=================+
y
and z
are stored in a different symbol table than x
and they are removed (together with the entire symbol table that contains them) when the call to f()
returns.
When y
is removed, its value is also removed because there is no name that points to it any more. But, because the value pointed by z
is also pointed by x
($z
is an alias), the value is not removed together with z
and it survives the function call. f()
modifies the value using $z
; and this change is visible in the main program through the variable $x
.
The things happen in a similar way when a function returns a reference. The function returns a value that is not copied but a new name that points to it is created into the symbols table of the calling code.