Return by reference in PHP
Easy rule: if you place an & in front of a function name in PHP you can use & when you call the function (ie. it’s return value) as if it was a variable and not a function.
It’s easier to think of it as «PHP allowing you to reference the return value» rather then PHP returning by reference. As mentioned above you simply place an & to tell PHP you want this, if you don’t place it and try do to $var =& somefunction() you will get the error «Only variables should be assigned by reference.»
To clear up some confusion with Jon’s answer. There is actually no need to have two separate functions one returning by reference and one returning by value; outside of some project convention. The & only allows it to return by reference, it doesn’t force it to return by reference.
eg. same function used both for reference and non-reference assignments
\header('content-type: text/plain'); class Demo < protected $example = 'original value'; function & example() < return $this->example; > > $demo = new Demo; #1 - pass by value $var1 = $demo->example(); $var1 = 'var1 value'; echo '$demo->example() => '.$demo->example().PHP_EOL; echo PHP_EOL; #2 - pass by reference $var2 =& $demo->example(); $var2 = 'var2 value'; echo '$demo->example() => '.$demo->example().PHP_EOL; echo '$var1 => '.$var1.PHP_EOL; echo '$var2 => '.$var2.PHP_EOL; echo PHP_EOL; #3 - altering other references $var3 =& $demo->example(); $var3 = 'var3 value'; echo '$demo->example() => '.$demo->example().PHP_EOL; echo '$var1 => '.$var1.PHP_EOL; echo '$var2 => '.$var2.PHP_EOL; echo '$var3 => '.$var3.PHP_EOL; echo PHP_EOL; exit;
$demo->example() => original value $demo->example() => var2 value $var1 => var1 value $var2 => var2 value $demo->example() => var3 value $var1 => var1 value $var2 => var3 value $var3 => var3 value
References are great however if you’re not familiar with them please follow the following rules:
- only consider using references if you are dealing with large array or similar non-object data structures
- never reference an object (if you can avoid it)
- only allow a function to pass by reference if the value is maintained in a static property or attribute on the object (ie. it’s not temporary variable inside the function)
Exceptions would obviously be very utilitarian uses (ie. when you actually want to share a value to simplify code for manipulating it).
Again, NEVER REFERENCE OBJECTS (it’s pointless!)
Suppose you have this class:
class Fruit < private $color = "red"; public function getColor() < return $this->color; > public function &getColorByRef() < return $this->color; > >
The class has a private property and two methods that let you access it. One returns by value (default behavior) and the other by reference. The difference between the two is that:
- When using the first method, you can make changes to the returned value and those changes will not be reflected inside the private property of Fruit because you are actually modifying a copy of the property’s value.
- When using the second method, you are in fact getting back an alias for Fruit::$color — a different name by which you refer to the same variable. So if you do anything with it (including modifying its contents) you are in fact directly performing the same action on the value of the property.
Here’s some code to test it:
echo "\nTEST RUN 1:\n\n"; $fruit = new Fruit; $color = $fruit->getColor(); echo "Fruit's color is $color\n"; $color = "green"; // does nothing, but bear with me $color = $fruit->getColor(); echo "Fruit's color is $color\n"; echo "\nTEST RUN 2:\n\n"; $fruit = new Fruit; $color = &$fruit->getColorByRef(); // also need to put & here echo "Fruit's color is $color\n"; $color = "green"; // now this changes the actual property of $fruit $color = $fruit->getColor(); echo "Fruit's color is $color\n";
See it in action.
Warning: I feel obliged to mention that references, while they do have legitimate uses, are one of those features that should be used only rarely and only if you have carefully considered any alternatives first. Less experienced programmers tend to overuse references because they see that they can help them solve a particular problem without at the same time seeing the disadvantages of using references (as an advanced feature, its nuances are far from obvious).
The example you posted is probably from PHP4 or earlier. It’s no longer necessary to return objects by reference as they are almost always done that way automatically in PHP5+.
Returning by reference is useful when you want to use a function to find to which variable a reference should be bound. Do not use return-by-reference to increase performance. The engine will automatically optimize this on its own. Only return references when you have a valid technical reason to do so.
See return references in PHP docs
Php return this by reference
points to post below me.
When you’re doing the references with loops, you need to unset($var).
In reply to lars at riisgaardribe dot dk,
When a variable is copied, a reference is used internally until the copy is modified. Therefore you shouldn’t use references at all in your situation as it doesn’t save any memory usage and increases the chance of logic bugs, as you discoved.
I think a correction to my last post is in order.
When there is a constructor, the strange behavior mentioned in my last post doesn’t occur. My guess is that php was treating reftest() as a constructor (maybe because it was the first function?) and running it upon instantiation.
class reftest
public $a = 1 ;
public $c = 1 ;
public function __construct ()
return 0 ;
>
$reference -> reftest ();
$reference -> reftest2 ();
echo $reference -> a ; //Echoes 2.
echo $reference -> c ; //Echoes 2.
?>
In this example class name is different from its first function and however there is no construction function. In the end as you guess «a» and «c» are equal. So if there is no construction function at same time class and its first function names are the same, «a» and «c» doesn’t equal forever. In my opinion php doesn’t seek any function for the construction as long as their names differ from each others.
class reftest_new
<
public $a = 1 ;
public $c = 1 ;
$reference = new reftest_new ();
$reference -> reftest ();
$reference -> reftest2 ();
echo $reference -> a ; //Echoes 2.
echo $reference -> c ; //Echoes 2.
?>
It matters if you are playing with a reference or with a value
Here we are working with values so working on a reference updates original variable too;
$b++;
echo «$a, $b»;//Output: 2, 2 both values are updated
$b = 10;
echo «$a, $b»;//Output: 10, 10 both values are updated
$b =$c; //This assigns value 2 to $b which also updates $a
echo «$a, $b»;//Output: 22, 22
But, if instead of $b=$c you do
$b = &$c; //Only value of $b is updated, $a still points to 10, $b serves now reference to variable $c
The order in which you reference your variables matters.
echo $a1 ; //Echoes «One»
echo $b1 ; //Echoes «One»
echo $a2 ; //Echoes «Four»
echo $b2 ; //Echoes «Four»
?>
If you set a variable before passing it to a function that takes a variable as a reference, it is much harder (if not impossible) to edit the variable within the function.
foo ( $unset );
echo( $unset );
foo ( $set = «set\n» );
echo( $set );
It baffles me, but there you have it.
In reply to Drewseph using foo($a = ‘set’); where $a is a reference formal parameter.
$a = ‘set’ is an expression. Expressions cannot be passed by reference, don’t you just hate that, I do. If you turn on error reporting for E_NOTICE, you will be told about it.
Resolution: $a = ‘set’; foo($a); this does what you want.
Here’s a good little example of referencing. It was the best way for me to understand, hopefully it can help others.
When using references in a class, you can reference $this-> variables.
class reftest
public $a = 1 ;
public $c = 1 ;
$reference -> reftest ();
$reference -> reftest2 ();
echo $reference -> a ; //Echoes 2.
echo $reference -> c ; //Echoes 2.
?>
However, this doesn’t appear to be completely trustworthy. In some cases, it can act strangely.
class reftest
public $a = 1 ;
public $c = 1 ;
$reference -> reftest ();
$reference -> reftest2 ();
echo $reference -> a ; //Echoes 3.
echo $reference -> c ; //Echoes 2.
?>
In this second code block, I’ve changed reftest() so that $b increments instead of just gets changed to 2. Somehow, it winds up equaling 3 instead of 2 as it should.
I discovered something today using references in a foreach
echo $a1 [ ‘a’ ]; // will echo b (!)
?>
After reading the manual this looks like it is meant to happen. But it confused me for a few days!
(The solution I used was to turn the second foreach into a reference too)
An interesting if offbeat use for references: Creating an array with an arbitrary number of dimensions.
For example, a function that takes the result set from a database and produces a multidimensional array keyed according to one (or more) columns, which might be useful if you want your result set to be accessible in a hierarchial manner, or even if you just want your results keyed by the values of each row’s primary/unique key fields.
function array_key_by ( $data , $keys , $dupl = false )
/*
* $data — Multidimensional array to be keyed
* $keys — List containing the index/key(s) to use.
* $dupl — How to handle rows containing the same values. TRUE stores it as an Array, FALSE overwrites the previous row.
*
* Returns a multidimensional array indexed by $keys, or NULL if error.
* The number of dimensions is equal to the number of $keys provided (+1 if $dupl=TRUE).
*/
// Sanity check
if (! is_array ( $data )) return null ;
// Allow passing single key as a scalar
if ( is_string ( $keys ) or is_integer ( $keys )) $keys = Array( $keys );
elseif (! is_array ( $keys )) return null ;
// Our output array
$out = Array();
// Loop through each row of our input $data
foreach( $data as $cx => $row ) if ( is_array ( $row ))
// Loop through our $keys
foreach( $keys as $key )
$value = $row [ $key ];
if (!isset( $last )) // First $key only
if (!isset( $out [ $value ])) $out [ $value ] = Array();
$last =& $out ; // Bind $last to $out
>
else // Second and subsequent $key.
if (!isset( $last [ $value ])) $last [ $value ] = Array();
>
if (isset( $last ))
// At this point, copy the $row into our output array
if ( $dupl ) $last [ $cx ] = $row ; // Keep previous
else $last = $row ; // Overwrite previous
>
unset( $last ); // Break the reference
>
else return NULL ;
// A sample result set to test the function with
$data = Array(Array( ‘name’ => ‘row 1’ , ‘foo’ => ‘foo_a’ , ‘bar’ => ‘bar_a’ , ‘baz’ => ‘baz_a’ ),
Array( ‘name’ => ‘row 2’ , ‘foo’ => ‘foo_a’ , ‘bar’ => ‘bar_a’ , ‘baz’ => ‘baz_b’ ),
Array( ‘name’ => ‘row 3’ , ‘foo’ => ‘foo_a’ , ‘bar’ => ‘bar_b’ , ‘baz’ => ‘baz_c’ ),
Array( ‘name’ => ‘row 4’ , ‘foo’ => ‘foo_b’ , ‘bar’ => ‘bar_c’ , ‘baz’ => ‘baz_d’ )
);
// First, let’s key it by one column (result: two-dimensional array)
print_r ( array_key_by ( $data , ‘baz’ ));
// Or, key it by two columns (result: 3-dimensional array)
print_r ( array_key_by ( $data , Array( ‘baz’ , ‘bar’ )));
// We could also key it by three columns (result: 4-dimensional array)
print_r ( array_key_by ( $data , Array( ‘baz’ , ‘bar’ , ‘foo’ )));
Returning by reference
A final important thing to know about user functions and references is how to return values by reference. Unlike passing values by reference where you just specify the referenced nature of the parameter in the function definition, to properly return references you need to specify in the definition and at call time. To specify a function returns a reference, you place the ampersand reference operator before the function name, and to specify that you wish to reference the result of the function as opposed to copying it, you use the normal reference assign that you learnt earlier.
In that example we do not pass any parameters in by reference, but we do pass back and assign a reference. With that code, you could remove the references entirely and it would still do exactly the same thing. However, the ability to return references becomes more important if, say, you were to return an element in an array — with references, you could change the element that gets returned, and it would change the array; without, you would get a copy of the element back, and any changes you made would not be reflected in the original.
Want to learn PHP 7?
Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!
If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!