Php Object Assignment By Reference

An issue to consider when programming is how variables are passed from one context to another. For example, the following code calls a function, referencing a variable in doing so:

$it = 'Larry'; some_function($it);

Will the function receive just the value of that variable (Larry) or will it receive the variable itself? The answer depends upon how the function is defined. In this post, I’ll explain this concept further, in not too-technical terms.Starting with the inner-workings of a programming language, you have to understand that there are several aspects to a variable. In PHP, when a variable is assigned a value, you have three things:

  • The variable’s name (like it in the above example)
  • The variable’s value (Larry)
  • Memory allotted on the computer, where that value will be stored.

(There’s a bit more going on but…)

Because PHP takes care of all the dirty work (and is generally an easy language to use), you don’t need to concern yourself with this last thing. All you have to do is refer to a variable (after it’s been assigned a value) to retrieve that value. By default, when you use that variable in a function call as in the above, you’re passing the value of the variable to the function, not the variable itself. In other words, in that above list of three things, only the middle thing—the value—is received by the function. Take this example:

$it = 'Larry'; function say_hello ($thing) { echo "Hello, $thing"; } say_hello($it);

That code is functionally equivalent to

say_hello('Larry');

An important consideration when it comes to passing variables by value is that if the function changes the value of $thing, the $it variable outside of the function is unaffected:

$it = 'Larry'; function say_hello ($thing) { echo "Hello, $thing"; $thing = 'World'; } say_hello($it); say_hello($it);

Those two function calls will print the same Hello, Larry message twice.

So that’s passing by value in a nutshell, which again is the default behavior (most of the time; more on exceptions later). PHP also allows you to pass variables by reference instead of by value. You can do so by changing the function definition, prepending the variable(s) to be received by reference with an ampersand:

$it = 'Larry'; function say_hello (&$thing) { echo "Hello, $thing"; $thing = 'World'; } say_hello($it); say_hello($it);

In that example, the actual variable is being passed to the function (in layman’s terms), not its value. As the PHP manual nicely describes it, both variables (i.e., $thing and $it) will be pointing to the same content, which is to say, pointing to the same place where that value is stored (similar to, but not exactly the same as pointers in C/C++).

Passing by reference also means that when the second variable’s value is changed, it’s changed for the original variable, too. In the most recent example, Hello, Larry will be printed once, followed by Hello, World, and $it outside of the function now has a value of World.

So that’s the fundamentals of passing by value vs. passing by reference. I don’t want to extend this too far, so as not to end up confusing you, but I’ll mention a couple of other random bits:

  • Older versions of PHP allowed you to pass a variable by reference in the function call: say_hello(&$it), but this has been phased out.
  • Some argue that passing by reference is faster for more complicated variables (like arrays and objects), but it’s best not to use passing by reference just to improve performance. PHP does lots of performance optimization on its own.
  • You can pass variables by reference not just in function calls, as in my examples, but also when using the assignment operator: $a =& $b. Now a change to the value of $a or $b will affect the value of the other.
  • As of PHP 5, objects are passed by reference by default when using the assignment operator. In PHP 4, they were passed by value.
  • For a great description of the inner workings involved, see Derick Rethans article.

Finally, for an interesting real-world look at how this affects PHP programming, take an example where you’re using prepared statements with a database, like MySQL:

$stmt = mysqli_prepare($dbc, 'INSERT INTO tablename (something) VALUES (?)'); mysqli_stmt_bind_param($stmt, 's', $somevar); $somevar = 'blah'; mysqli_stmt_execute($stmt);

The mysqli_stmt_bind_param() function receives the variables by reference, which is why the variable can (and often is) assigned a value after it’s named in the function call, but before the mysqli_stmt_execute() function call. Once the execute function is called, PHP will grab the value of the variable through the reference, to use in the query.

PHP Clone and Shallow vs Deep Copying

Looking through the PHPExcel library recently I saw how Mark Baker overrides the magic method with comments about ensuring that cloning will perform a deep copy instead of a shallow one. This is a topic with which I had not had much experience so I did some research and experimenting to learn what happens when you use and the difference between a shallow copy and a deep copy.

First I examined how you might copy variables in general. Let’s start with the basics.

is a copy of . Incrementing has no effect on . If we wanted different behavior we would need to copy by reference with the use of an ampersand.

Now when we increment we are also incrementing . Well, not really. , the variable, is a handle or reference to where the data is stored in memory. In the first example we copy by value which (more or less) makes a new entry in memory and is a handle to that new location. In the second example simply copies the handle. There is only one entry in memory and both and point to the same location. What we are actually doing when we increment is incrementing the data referenced by . references the same data.

Next I looked at how to copy objects. I started with this simple class Project.

I repeated the same copy assignment from Example 1.

In the previous examples we would have two completely distinct variables that reference two different addresses in memory. So to see if this holds true we can make a change in and see the impact.

Based on Example 1 we might expect that this would print the original description we set in . In fact, it will actually print the new description. It appears that the objects are copied by reference. One of the things you will often hear is that in PHP 5 objects are copied by reference by default. Some research shows that isn’t quite true either. Consider this explanation from PHP.net

A PHP reference is an alias, which allows two different variables to write to the same value. As of PHP 5, an object variable doesn’t contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.

This means copying an object by value will copy the identifier… by value. This behaves very similarly to copying by reference because the copied identifier points to the same object but is not exactly the same. For a more detailed explanation of the difference you can visit the source link.

So what if you don’t want this behavior? Here enters the keyword.

Now $project_two is a clone of and is its own honest object. When we repeat the test with the descriptions we will see that monkeying around with ’s description has no effect on .

What happens if the property of a Project is another object? To test this I created a new class Person and modified Project to hold a Person

I then made a Project and set its Lead Developer.

As is a clone, setting a new value for did not change . Great, right? Let’s look at this from a different angle.

The plot thickens. Here we can see that and both point to the same object. $project_one and $project_two are both unique objects but they do not have unique values for properties that are references or object identifiers. This is what is meant by a shallow copy. If we wanted the two projects to be completely separate we would need to change the default behavior of clone to perform a deep copy instead of a shallow copy. To do this I define the magic method.

After PHP clones an object it will attempt to call the method on the new cloned object. Now when we perform the previous test we can see that and point to two completely different objects. This is a deep copy.

Note that a common technique for ensuring a deep copy is to serialize then immediately unserialize each of a class’s object properties. Take for example this code from PHPExcel

This method forces PHP to split apart any object properties from their references and return new instances.

Additional information and examples about cloning can be found on php.net


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *