To the C/C++ coder, PHP references are bit challenging to understand. Are they pointers?
Regardless of the way references do truly work, what confuses the C/C++ coder is that it seems that PHP references are not like pointers because of the syntax:
int i, *p; i = 0; p = &i; std::cout << *p; // In PHP, you would write p and not *p
As in :
$i = 0; $p = &$i; echo ($p); // See: that's $p as if it was like $i
So, how should the C/C++ coder understand how references do work in PHP ? The purpose of this article is to cast some light on this. More broadly speaking, anyone who intends to use references in PHP should find something interesting here - most notably to clarify the relationship between reference and
unset ()
.
Ciquez ici pour lire cet article en français.
PHP variables are (sort of) pointers...
Everybody knows that this can be clarified by writing the
&
after the =
and not before the variable name:
$i = 0; $p =& $i; echo ($p);
Because the C/C++ coder may now understand that the
&
is not the &
of C/C++, but part of a special operator. From this point on, the C/C++ coder can consider that PHP variables are nothing more than pointers. Here is how he will mentally translate PHP code in C/C++:
$a = 0; // *a = 0 $b =& $a; // b = a $b = 1; // *b = 1 and so *a = 1
...or pointers of pointers
Objects are bit special. A PHP variable does not contain the object itself, but an identifier for the object. That's why:
class Bird { public $name = 'The Great Eagle'; } $b0 = new Bird (); $b1 =& $b0; echo ($b1->name); // This works $b0 = NULL; echo ($b1->name); // This fails
The C/C++ coder may consider that such a variable is a pointer to a pointer, which is very common in those languages:
#include <iostream> class Bird { public : char name[16] = "The Great Eagle"; }; int main() { Bird *_b0, **b0, **b1; _b0 = new Bird (); b0 = &_b0; std::cout << (*b0)->name; // Success b1 = b0; *b0 = NULL; std::cout << (*b1)->name; // Failure (at run-time) }
Learn to think PHP!
The big surprise is that this way of reading PHP code from the C/C++ coder point of view does not make us think about PHP references as C/C++ pointers, but to think of PHP variables as C/C++ pointers or pointers of pointers.
Indeed, as explained in the PHP documentation "variable name and variable content are different, so the same content can have different names".
The compatible idea suggested in this article is that the name of a variable may be considered as a smart pointer (as long as a name is binded to a content, this content does exist). For this reason, the following code works, whatever is the type of the variable (number, string, array, object) :
$a = 'Hello, world:'; $b =& $a; $b = 'Bye-bye, world!'; echo ($a); unset ($a); // The string stil exists, because the name "b" is binded to it echo ($b); unset ($b); // The string does not existe anymore, because no name is binded to it
Bye-bye, world! Bye-bye, world!
This may help, but in the end, the C/C++ coder should not forget translating code like this does not tell how references truly work in PHP. They are not pointers, but aliases, so thinking of them as pointers may prove misleading in the end.
The fact is that if you want to learn a language, you have to get thinking in that language, which may be difficult because you have to stop thinking in your own language.
More about functions
You can pass a variable by reference to a function. For this purpose, a leading
&
must be added to the name of the argument (not in the function call!) :
function f (&$value) { global $speech; $value = 'Bye-bye, world!'; $value =& $speech; } $speech = 'Ich bin ein Berliner'; $message = 'Hello, world!'; f ($message); echo ($message);
In this example, using
=&
to assign a reference of $speech
to $value
does not affect $message
. That is because the name "value
" has been binded to the content to which the name "speech
" is already binded : the name "message
" has never been involved.
Notice that a variable containing the instance of a class (an object) is always passed by reference. To understand why, this sentence must be rephrased more rigorously, as explained in the PHP documentation. As previously mentioned, in the case of the instance of a class, the variable contains an identifier for the object and not the object itself. Thereby, when the function is called, the identifier is copied into the argument, and everything happens as if the variable presumed to contain the object had been passed by reference:
class Bird { public $name='The Great Eagle'; } function f ($value) { $value->name = 'The Crow'; } $b = new Bird (); f ($b); echo ($b->name);
The Crow
Moreover, a function may return a reference. A leading
&
must be added to the name of the function, and the returned value must assigned with =&
if you wish to use it as a reference (if not, the content will be copied) :
$speech = 'Ich bin ein Berliner'; function &f () { global $speech; $value =& $speech; return ($value); } echo ($speech); $message =& f (); // The content binded to "speech" is now binded to "message" $message = 'Hello, world!'; // The content binded to "speech" is updated echo ($speech); unset ($message); // Unbind "message" or the next assignement will update the content binded to "speech"! $message = f (); // The content binded to "speech" is copied and binded to "message" $message = 'Bye-bye, world!'; // The content binded to "speech" is not updated echo ($speech);
Ich bin ein Berliner Hello, world Hello, world
The previous example shows that you must be really careful when using references. The PHP documentation calls attention about this regarding
foreach
. After a foreach
, you shall use unset ()
to unbind the name and the last entry of the array if you wish to use this name again :
$values = [0, 1, 2]; foreach ($values as $key => &$value) {} $value = 3; // Updates $values[2] to which "value" is still binded! echo ($values[3]);
3
What the C/C++ coder must assimilate
To understand the concept of reference in PHP, the C/C++ coder must fight against himself to assimilate two things :
-
There is a difference between the name of variable and the content of this variable. Several names may be used to address a same content to which they are binded. As long as at least one name is binded to it, this content does exist. A name and a content can be unbinded by calling
unset ()
. - In the case of an object (an instance of a class), the variable name is not binded to the object itself, but to an identifier for this object.