<

PHP Variables and References

Published on
25,553 Points
3,053 Views
5 Endorsements
Last Modified:
Awarded
Before You Read The Article
Please make sure you understand these two concepts: Variable Scope and Property Visibility.  And to save time later, you might want to bookmark the official PHP explanation of References.  However, please be aware of the age of some of the comments in the PHP.net web site.  The PHP object model has evolved considerably over the years, and the older comments may not reflect the true behavior of the current stable release.

Variable Assignment
In procedural (non-object-oriented) PHP, variable assignment takes place with the single equal sign.  When a variable is assigned this way, the contents of the variable named on the left side of the equal sign is replaced with the contents of the expression on the right side of the equal sign.  For example, the code below assigns the value of 3 to a variable named $x, then the value in $x is assigned to a variable named $y.  At this point in the script we have two variable containers that are separate and distinct.  A change to either $x or $y will not affect the other variable,  This is simple, straightforward and a common-sense example.
 
<?php
$x = 3;
$y = $x;
$y++;
var_dump($x); //  int(3)

Open in new window

If it always worked that way (like in Java) that would be fine with me, but PHP has a few "wrinkles" in its variable assignment.  To understand this better, you need to think of a variable name as if it were a pointer to a box.  Whatever you put into the box, can be retrieved or changed by making reference to the variable name.  And in PHP you can have more than one variable name pointing to the same box.

Reference Assignment
In the next code sample, you can see that we used the equal-ampersand in the assignment statement for $y, and by doing that we told PHP to not create another variable box, but instead to point the $y variable pointer to the same box that the $x variable pointer is already pointing to.  The ampersand is referred to as the "reference sign."  Now we have two variable name pointers that point to the same box and it follows that a change to either variable $x or $y will change the contents of the box.
 
<?php
$x = 3;
$y =& $x;
$y++;
var_dump($x); // int(4)

Open in new window

Could this be confusing? Yep, especially if you are used to the concept that a variable name is a unique pointer to a variable value (the contents of the box).  But that cannot be assumed.  PHP permits an unlimited number of variable names to point to the same box containing the variable value.
http://php.net/manual/en/language.references.whatare.php
http://php.net/manual/en/language.references.arent.php#85533

Variable Un-Assignment
PHP unset() removes the variable name, but not the variable value.  If you have a proliferation of variable names, in conjunction with changes in scope or namespace, this behavior could lead to a memory leak. In the example below, even after we unset $y, we still have $x taking up storage.
 
<?php
$x = 3;
$y =& $x;
unset($y);
var_dump($x, $y); // int(3) NULL

Open in new window


Don't Let It Get Complicated
What if you were to write something like the example below?  What would happen with the multiplication during the assignment of the $y reference?  Nothing.  PHP does not throw a parse error and no multiplication occurs.  Don't write something like this!
 
<?php
$x = 3;
$y =& $x * 2;
var_dump($x, $y); // int(3) int(3)
$y++;
var_dump($x, $y); // int(4) int(4)

Open in new window

Executive Summary:  Assignment statements using the equal sign produce a new box with new contents and a new variable name that points to the box.  Assignment statements using the equal-ampersand produce a new variable name, but not a new box.  They simply point the new variable name to the original box.  Except in object-oriented PHP, which we will explain below.

Encapsulation of Variables in Functions
In PHP functions, the external variables (function arguments) are, by default, copied and made available to the code inside the function definition.  This means that variables inside the function are separate from variables in the outer scope of the script, and name collisions cannot occur.  This is called "passing variables by value."  However it is possible, using the ampersand notation, to pass variables by reference.  Variables that are passed to functions by reference are mutable within the scope of the function, and the mutation is also produced in the external scope.
 
<?php
function x(&$var)
{
    $var .= ' changed';
}
$y = 'This value';
x($y);
var_dump($y); // string(18) "This value changed"

Open in new window

Most of the built-in PHP functions pass arguments by value.  However some built-in PHP function definitions contain the ampersand!  PHP array sorting functions all work this way.

How can you know which PHP functions pass arguments by reference?  It's not as simple as it might be, and to compound the problem, PHP changed the rules about reference notation at PHP5.3 and PHP5.4.  Today, there is no reference sign (ampersand) in a function call; it can only be found in the function definitions.  Look for the ampersand in the PHP online documentation in the Description.  For example, this from the sort() function:

bool sort ( array &$array [, int $sort_flags = SORT_REGULAR ] )

As if you needed another reason to read the online manual!  And if you're working with user functions that are not a native part of PHP, you must find and read the function definition to understand what the function might be injecting into your variable scope.

Array and Object Variables in Iterators
Iterators, in particular foreach() operate on arrays in a way that appears to combine passing by value and passing by reference.  When you write a foreach() statement, PHP accesses the input array once each time foreach() is executed, getting the key and value from the array and putting these into the variables in the foreach() definition.  Thus you can modify the content of the original array with something like this:
 
<?php
$arr = array
( 'A' => 1
, 'B' => 2
, 'C' => 3
)
;

foreach ($arr as $key => $num)
{
    if ($key == 'A') unset($arr[$key]);
    if ($key == 'B') $arr[$key]++;
}
print_r($arr); // Array ( [B] => 3 [C] => 3 )

Open in new window

It also works with objects.
 
<?php
$obj = new stdClass;
$obj->A = 1;
$obj->B = 2;
$obj->C = 3;

foreach ($obj as $key => $num)
{
    if ($key == 'A') unset($obj->$key);
    if ($key == 'B') $obj->$key++;
}
print_r($obj); // stdClass Object ( [B] => 3 [C] => 3 )

Open in new window

But there can be surprises if you write your foreach() statement in combination with a reference assignment!  You can find an excellent explanation of this rather strange behavior on Johannes Schleuters' blog.  It has been reported as a bug repeatedly since 2004, but it is not, in fact, a bug (just a strange design element in the language).  To avoid this behavior, do not use references with foreach().
 
<?php
$arr = array
( 'A' => 1
, 'B' => 2
, 'C' => 3
)
;

foreach ($arr as $key => &$num) { }
foreach ($arr as $key =>  $num) { }
print_r($arr); // Array ( [A] => 1 [B] => 2 [C] => 2 )

Open in new window


"Copying" Arrays and Objects
The PHP Introduction to Classes and Objects says, "PHP treats objects in the same way as references or handles, meaning that each variable contains an object reference rather than a copy of the entire object."  No ampersands anywhere in sight!  Watch what happens when we copy the array variable with an assignment statement, then change the copy.  The original remains intact.  But when we "copy" the object the same way, we do not get a copy of the object.  We get a copy of the variable pointer to the original object.  And changes to the object can be made through the use of either variable pointer.
 
<?php
$arr = array
( 'A' => 1
, 'B' => 2
, 'C' => 3
)
;

$bar = $arr;
unset($bar['B']);
print_r($arr); // Array ( [A] => 1 [B] => 2 [C] => 3 )

$obj = new stdClass;
$obj->A = 1;
$obj->B = 2;
$obj->C = 3;

$bbj = $obj;
unset($bbj->B);
print_r($obj); // stdClass Object ( [A] => 1 [C] => 3 )

Open in new window

The object assignment contains an implicit ampersand, creating an alias for the original object.  How, then, can we get a duplicate object, one that is not a reference, but a whole new data element?  There are two ways: instantiation and cloning.

Object Instantiation
A new object is created from a class definition by the keyword "new" and the object that is created this way is an initialized version of the object, containing only what the class defined.  Many objects can be created from the same class, and each such new object variable name points to a unique object in memory.
 
<?php
$x = new stdClass;
$y = new stdClass;

var_dump($x, $y); // object(stdClass)#1 (0) { } object(stdClass)#2 (0) { }

Open in new window

As we read the var_dump() output in the snippet above, we can see that $x is an object instance of stdClass, and it is object #1.  $y is also an object instance of stdClass, and it is object #2.  Separate and identical objects -- just what we wanted.

Cloning
But what if we want to replicate an object, not in its original state, but after some changes had been made to the object?  Here we can see the use of the "clone" keyword to make a replicant from the modified object.  The process of cloning need not simply copy the object; if the __clone() method is defined, cloning can invoke the magic method __clone() to provide additional logic at the time that the object is cloned.
 
<?php
$x = new stdClass;
$x->A = 3;

$y = clone $x;
$y->B = 4;

var_dump($x, $y); // object(stdClass)#1 (1) { ["A"]=> int(3) } object(stdClass)#2 (2) { ["A"]=> int(3) ["B"]=> int(4) }

Open in new window


Summary
In this article we have seen examples of variable assignment, passing variable values, passing variable references, the explicit use of the ampersand to designate pass-by-reference (instead of value) and the implicit use of the ampersand that PHP invokes in some of its function definitions and all of its object assignments.  We have seen how to truly copy an object, making a new object variable, both in its original state and in its current state.  

Bringing it all together, one of the best explanations on the PHP.net site is here:
http://php.net/manual/en/language.oop5.references.php#101900

And an excellent summary of the terms (right here on E-E) is available here:
http://www.experts-exchange.com/Programming/Languages/Scripting/PHP/Q_28466835.html#a40168443

Please give us your feedback!
If you found this article helpful, please click the "thumb's up" button below. Doing so lets the E-E community know what is valuable for E-E members and helps provide direction for future articles.  If you have questions or comments, please add them.  Thanks!
 
5
Comment
Author:Ray Paseur
3 Comments
 
LVL 75

Expert Comment

by:käµfm³d 👽
Ray,

If you're going to mention cloning, then I think it's beneficial to make mention of the "shallow copy" that is performed (per your "clone" link). This can be a source of confusion for new developers of any language which supports the construct.
0
 
LVL 111

Author Comment

by:Ray Paseur
@kaufmed: Excellent point!  The PHP.net __clone() man page says this:
When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties. Any properties that are references to other variables, will remain references.  Once the cloning is complete, if a __clone() method is defined, then the newly created object's __clone() method will be called, to allow any necessary properties that need to be changed.
Possibly the __clone() magic method could be exploited to accomplish deep copying when that is the desired outcome.  A good explanation with examples in different programming languages is available here.
0
 

Expert Comment

by:rgb192
I googled
ray paseur reference clone
and
http://www.experts-exchange.com/Programming/Languages/Scripting/PHP/Q_28232255.html#a39533264

that this question was made for me.

I copy pasted all the code blocks because I am doing a lynda.com tutorial which uses the words reference and clone
So I am re learning all the Ray code.
Thanks.
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Join & Write a Comment

The viewer will learn how to count occurrences of each item in an array.
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
Suggested Courses
Course of the Month15 days, 16 hours left to enroll

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month