PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1
CHAPTER 4 ■ ADVANCED FEATURES

Copying Objects with __clone()


In PHP 4, copying an object was a simple matter of assigning from one variable to another.


class CopyMe {}
$first = new CopyMe();
$second = $first;
// PHP 4: $second and $first are 2 distinct objects
// PHP 5 plus: $second and $first refer to one object


This “simple matter” was a source of many bugs, as object copies were accidentally spawned when
variables were assigned, methods were called, and objects were returned. This was made worse by the
fact that there was no way of testing two variables to see whether they referred to the same object.
Equivalence tests would tell you whether all fields were the same (==) or whether both variables were
objects (===), but not whether they pointed to the same object.
In PHP, objects are always assigned and passed around by reference. This means that when my
previous example is run with PHP 5, $first and $second contain references to the same object instead of
two copies. While this is generally what you want when working with objects, there will be occasions
when you need to get a copy of an object rather than a reference to an object.
PHP provides the clone keyword for just this purpose. clone operates on an object instance,
producing a by-value copy.


class CopyMe {}
$first = new CopyMe();
$second = clone $first;
// PHP 5 plus: $second and $first are 2 distinct objects


The issues surrounding object copying only start here. Consider the Person class that I implemented
in the previous section. A default copy of a Person object would contain the identifier (the $id property),
which in a full implementation I would use to locate the correct row in a database. If I allow this property
to be copied, a client coder can end up with two distinct objects referencing the same data source, which
is probably not what she wanted when she made her copy. An update in one object will affect the other,
and vice versa.
Luckily you can control what is copied when clone is invoked on an object. You do this by
implementing a special method called clone() (note the leading two underscores that are
characteristic of built-in methods).
clone() is called automatically when the clone keyword is invoked
on an object.
When you implement clone(), it is important to understand the context in which the method
runs.
clone() is run on the copied object and not the original. Here I add __clone() to yet another
version of the Person class:


class Person {
private $name;
private $age;
private $id;


function __construct( $name, $age ) {
$this->name = $name;
$this->age = $age;
}


function setId( $id ) {
$this->id = $id;
}

Free download pdf