One of the things that seems to confuse people most with PHP, whether it’s their first programming language or they come from a C++ or Java background, is how variable references work. Wherever & is found doubt and uncertainly seem to follow...
References are actually very easy to understand. This article takes another shot at explaining them.
PHP and Variables
Forgetting about references for a moment, the first thing to be aware of is what PHP normally does with variables as they move around our code.
PHP's Default Copying Behaviour
Whenever we do any of the following, PHP makes a copy of the contents of the variable;
Assign one variable to another
e.g.
<?php $color='blue'; $settings['color']=$color; ?>
$settings[’color’] now contains a copy of $color, i.e. the value ‘blue’.
Call a function, passing it a variable
e.g.
<?php
function primaryColor ($color) {
$primary=array('red','green','blue');
if (in_array($color,$primary))
return true;
else
return false;
}
$myColor='blue';
if (primaryColor($myColor) ) {
echo ($myColor.' is a primary color');
} else {
echo ($myColor.' is not a primary color');
}
?>
In the above example we’ve defined our own function primaryColor(). When we call the function, PHP passes it a copy of the variable. I.e on this line on the primaryColor() function;
if (in_array($color,$primary))
$color is a copy of $myColor.
Call an object method, passing it a variable
This is basically an extension of the above example;
<?php class ColorFilter { var $color; function ColorFilter($color) { $this->color=$color; } function isPrimary () { $primary=array('red','green','blue'); if (in_array($this->color,$primary)) return true; else return false; } } $myColor='blue'; $filter=new ColorFilter($myColor); if ($filter->isPrimary($myColor) ) { echo ($myColor.' is a primary color'); } else { echo ($myColor.' is not a primary color'); } ?>
At all times, with the ColorFilter class, it uses a copy of $myColor.
So far so good. We know know that by default PHP makes copies of a variables contents whenever they are passed to anything else.
Introducing References
Using a reference, we tell PHP that instead of copying the contents of variable, it should simply refer to the origional value. We can pass by reference using the ampersand operator &, for example;
<?php $color='blue'; $settings['color']=& $color; // Passed by reference ?>
$settings[’color’] now contains a reference to the contents of $color rather than a copy.
“So what?” we could reasonably ask. In this simple example, it makes no difference whether we use a reference or a copy.
Why References are Important
For most situations, using a copy of a variables contents is fine. But by copying a variables contents, what do we do if the origional variable changes after it has been passed?
That may seem like an obscure point but the best way to illustrate it is with some examples in PHP - we’ll see why references are important.
Compare these two snippets of PHP;
First using the default copying behaviour;
<?php $color='blue'; $settings['color']= $color; // Makes a copy $color='red'; // $color changes echo ( $settings['color'] ); // Displays "blue" ?>
Now passing by reference;
<?php $color='blue'; $settings['color']=& $color; // Makes a reference $color='red'; // $color changes echo ( $settings['color'] ); // Displays "red" ?>
Notice how when we pass by reference, after the $color changes, the change is reflected in $settings[’color’]?
Again this example is trivial and may not seem important. But now let’s look at what happens when we use objects...
References and Objects
We’ll demonstrate the point with three classes.
First we have a class used to store data; 1)
// Data acts as a store for $color and $size class Data { var $color; var $size; function Data () { $this->color='white'; $this->size='medium'; } function getColor() { return $this->color; } function getSize () { return $this->size; } function setColor ($color) { $this->color=$color; } function setSize ($size) { $this->size=$size; } }
Now we make another class ControlPanel which we use for changing application settings, passing it a refernce to in instance of the Data class;
// ControlPanel changes settings in the application class ControlPanel { var $data; // An instance of the Data class // Receive a reference to an instance of Data function ControlPanel (& $data) { $this->data=& $data; // Pass by reference again } function changeColor ($color) { $this->data->setColor($color); } function changeSize ($size) { $this->data->setSize($size); } }
Finally we make a class Output we use for rendering output to the end user. We’ll pass it an instance of the Data class and to start with we’ll have it use PHP‘s default copying behaviour;
// Output deals with building content for display class Output { var $data; // An instance of the Data class var $output; function Output ($data) { $this->data=$data; // Copy the instance of Data } function buildOutput () { $this->output='Color is '.$this->data->getColor(). ' and size is '.$this->data->getSize(); } function display () { $this->buildOutput(); return $this->output; } }
Now let’s see these classes in action;
$data=new Data; // Create an instance of Data
$output=new Output ($data); // Pass it to an instance of Output
// Use ControlPanel to modify some settings
$cp= new ControlPanel($data);
$cp->changeColor('red');
$cp->changeSize('large');
// Display the output
echo ($output->display()); // Displays: 'Color is white and size is medium'
Notice what’s happened? When we used ControlPanel to change some settings in the instance of Data, the changes were not reflected in the instance of Output. That means, although we used ControlPanel to modify Data, Output displayed only the origional values not the modified values.
Now if we modify the Output class to use a reference to data instead;
// Output deals with building content for display class Output { var $data; // An instance of the Data class var $output; // Accept a reference to Data function Output (& $data) { $this->data=& $data; // Pass by reference } function buildOutput () { $this->output='Color is '.$this->data->getColor(). ' and size is '.$this->data->getSize(); } function display () { $this->buildOutput(); return $this->output; } }
Now when we uses these classes in our code;
$data=new Data; // Create an instance of Data
$output= new Output ($data); // Pass it to an instance of Output
// Use ControlPanel to modify some settings
$cp= new ControlPanel($data);
$cp->changeColor('red');
$cp->changeSize('large');
// Display the output
echo ($output->display()); // Displays: 'Color is red and size is large'
Now Ouput will reflect any changes we make to the instance of Data.
As we can see, references are important when using objects in PHP, because we don’t always know what’s happening behind the scenes. Without looking at the code for the classes, how would we have known the ControlPanel was modifying values in Data?
Notice that in the Output class we pass by reference twice;
// Accept a reference to Data
function Output (& $data) {
$this->data=& $data; // Pass by reference
}
The first pass by reference is in the methods argument, which makes $data available only within the scope of the Output constuctor method. To make the value of $data available to other methods in Output, we have to pass it again by reference to a local class variable (property).
Note: it’s good practice to use references when creating objects as well. So what we should have done in our example was;
$data=& new Data; // Create an instance of Data $output=& new Output ($data); // Pass it to an instance of Output // Use ControlPanel to modify some settings $cp=& new ControlPanel($data);
Although is doesn’t make any difference in this example, when using more complex applications (or in particular third party class libraries) we may run into situations where there is alot happening behind the scenes. Unless specifically told otherwise, if in doubt use references.
Copies, References and Performance Issues
How passing variables by reference or copy effects application performance is an interesting problem.
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.
Call Time Reference Passing
One thing to be aware of is passing references at “call time”, which is when we place the reference operator in the argument of a function call, for example;
primaryColor( & $color );
In general doing this is a bad idea, as it makes code a nightmare for other developers to understand and also defeats one of the key purposes of encapsulation in object oriented code. We should leave it to the primaryColor function to make the decision as to whether is wants a copy or a reference to $color.
In php.ini we find the setting;
allow_call_time_pass_reference = On
It’s a good idea to switch this off and modify our code to avoid the use of call time references. Once switched off, we’ll get error messages like;
Warning: Call-time pass-by-reference has been deprecated - argument passed by value
Returning References
It’s worth being aware that we can return references to values by placing the reference operation in the function declaration, e.g.;
function & timesTwo ($number) {
return $number * 2;
}
Using an example with objects this might be;
<?php class Message { var $msg = 'Hello World'; function setMsg ($msg) { $this->msg=$msg; } function getMsg () { return $this->msg; } } class MessageFactory { // Return a reference to an instance of Message function & createMessage () { return new Message; } } $factory= new MessageFactory; $msg=$factory->createMessage(); echo ($msg->getMsg()); ?>
Zend 2 and References
Having said all that, with Zend 2, the new engine for PHP (which looks like it may be officially release Summer 2003), the default behaviour of PHP looks like it will be always to pass by references, a “magic” function name __clone() existing for when we want copies instead.
Further Resources
PHP References explained at Zez BasicAdvanced References in PHP from OnLAMP.com. PHP 4: Reference Counting and Aliasing on Zend References Explained from the Manual