Question
What does the following code print out?
function arrayPrint($array)
{
echo implode(' ', $array);
}
$arrayA = [1, 2, 3];
$arrayB = $arrayA;
$arrayB[1] = 0;
arrayPrint($arrayA);
Answer
The answer to this question is that the code prints out "1 2 3".
The reason is that when you assign a variable to another variable in PHP you will get a copy of the original variable. Any action we then perform on the new property will not effect the original property.
The value contained in the property $arrayB in the above example is now "1 0 3", as we have set the array item at the index of 1 to be 0. Unless you specify otherwise, all arrays in PHP start at index 0, so the "second" item in the array has the key of 1.
We can demonstrate this copying of variables on assignment with integers using the following example.
$integerA = 1;
$integerB = $integerA;
$integerB = 2;
echo $integerA; // Prints "1".
echo $integerB; // Prints "2".
In the above code, changing $integerB doesn't effect the value of $integerA. This is essentially the same example as the original question, but in this case we are altering single variables so is is a little clearer to see what is going on.
We can change the behaviour of the assignment operator by asking to to assign a reference to $integerA. This is done by prefixing the "&" symbol to the start of the variable and using assignment by reference.
Here is the single variable example rewritten with this in mind.
$integerA = 1;
$integerB = &$integerA;
$integerB = 2;
echo $integerA; // Prints "2".
echo $integerB; // Prints "2".
When we assign &$integerA to $integerB we are really making $integerB a reference to the variable $integerA, which means that any change we make to either variable will be made to both. So, in the above example, setting $integerB to have the value "2", means that we are really setting $integerA to have the value of "2" as $integerB is a reference to the other variable.
Objects Are References
There is an exception to this rule in PHP, which is when we are dealing with objects. In PHP, all object variables are pointers to the original object. The "new" operator, used when creating an object, automatically returns a reference.
This means that if we assign the object to a different variable we create two pointers to the same object. We don't need to use the "&" symbol to do this.
As an example of this, let's take a simple class that has a publicly accessible property (with a default value of 0) and a method to print out the class properties.
class SomeClass
{
public $someProperty = 0;
public function __toString()
{
return 'someProperty: ' . $this->someProperty;
}
}
We then create an instance of this class in the $objectA variable and then assign it to the $objectB variable. After that, the $objectB variable is then used to set the property to a value of 1.
$objectA = new SomeClass();
$objectB = $objectA;
$objectB->someProperty = 1;
When we print out the object (with the help of the __toString() method) we see that the property has been set to 1 in both objects.
echo $objectA; // Prints "someProperty: 1"
echo $objectB; // Prints "someProperty: 1"
This happens because we have told PHP that the variables $objectA and $objectB both point to the same place in memory. So, when we set a property of one variable we are actually changing a single object.
If we want to copy the object into a different variable then we need to use the clone keyword. Using this keyword will cause PHP to make a copy of the original object in $objectA and point the property $objectB at this new object.
$objectA = new SomeClass();
$objectB = clone $objectA;
$objectB->someProperty = 1;
echo $objectA; // Prints "someProperty: 0"
echo $objectB; // Prints "someProperty: 1"
With this simple change in place the two variables act as different objects.
Passing By Reference
The referencing of variables in PHP is also an important consideration to make when passing variables to functions. In this case, variables are still passed by value and objects are passed by reference.
Take the following function, that accepts a string and attempts to set the string to a value.
function setString($string) {
$string = 'something';
}
$string = '';
setString($string);
echo $string // Prints ''.
This function actually does nothing as the $string variable inside the function is technically a new variable and so if we set a value to that variable it will only exist for as long as the function is being run.
If we update the function so that the $string variable is passed as a reference, then anything we do inside the function will alter the variable externally.
function setString(&$string) {
$string = 'something';
}
$string = '';
setString($string);
echo $string; // Prints 'something'.
This is useful if you want to perform operations on a variable directly, but this technique should be avoided if possible. A useful concept in functional programming is immutable functions. Functions should accept parameters and then return a result that should then be used to update the original variable. This means that any changes we make to the variable are done in one place, rather than spread across areas of the program.
To update the previous example to be immutable we would just return the changed string and update the original variable with our changed version.
function setString($string) {
$string = 'something';
return $string;
}
$string = '';
$string = setString($string);
echo $string // Prints 'something'.
Obviously, this is a silly example, you would have some sort of logic in the setString() function, but this shows how we can avoid confusion through passing by reference.
There is one good use of passing variable by reference. I recently was looking at how to create a recursive closure. To make a recursive closure in PHP you need to pass the closure variable by reference to the "use" global state of the closure.
Here is an example that works out the factorial of a number.
$factorial = function($n) use (&$factorial) {
if ($n == 1) {
return 1;
}
return $factorial($n - 1) * $n;
};
print $factorial(5); // Prints 120
If you don't pass the closure by reference then you will essentially pass a null value, since the closure hasn't been defined yet. Passing it by reference means you can use the variable within the closure once it has been created.
Add new comment