Modelo de objetos en PHP5

Constructor de copia

__clone()

Con el nuevo modelo de objetos de PHP5 las variables que representan objetos son en realidad referencias (manejadores, handlers) al objeto en sí.

Esta característica representa una ventaja con respecto a PHP4, ya que en la mayoría de las ocasiones no se necesita una copia de un objeto, sino simplemente una referencia al mismo. En PHP4 la única solución consistía en trabajar con referencias de forma explícita:

//// PHP4 - Referencias explicitas 

//// Al crear un objeto
$obj_vehiculo =& new Vehiculo();

//// En asignaciones
$vehiculo1 =& $vehiculo2;

//// En paso de parametros a funciones
function printVehiculo (&$vehiculo)

Sin embargo, en algunas ocasiones sí es útil la posibilidad de copiar (clonar) objetos. PHP5 introduce el método predefinido __clone(), disponible por defecto en todos los objetos y redefinible para cada clase.

Este método no puede ser llamado directamente, se activa utilizando la palabra reservada clone.

Si no se redefine el método clone en una clase, la copia del objeto se realiza bit a bit. Es decir, el objeto resultante es una copia exacta del original (que sería equivalente a la copia/asignación de objetos en PHP4):

//// PHP5 - copia de objetos
$vehiculo2 = clone vehiculo1;

Hay casos en los que interesará crear métodos __clone() a medida. Los siguientes ejemplos ayudarán a entender la utilidad del constructor de copia.

Se parte de una clase Moto que tiene como atributo un array de objetos Rueda. Es un ejemplo de composición: "una moto tiene ruedas". El objetivo es conseguir una nueva moto idéntica a moto1.

La asignación directa da como resultado una única moto con dos referencias:

//// PHP5 - asignacion de referencias
$moto2 = $moto1;

Con la copia bit a bit que proporciona el método __clone() por defecto tampoco resuelve el problema. Habrá dos motos, sí... pero comparten las mismas ruedas!

//// PHP5 - __clone por defecto
$moto2 = clone $moto1;

La solución está en redefinir el método __clone() para conseguir:

El código necesario se muestra a continuación. La clase Moto contiene un array de objetos Rueda:

//// clase Vehiculo
class Vehiculo
{
public
$peso;
public
$potencia;
}


//// clase Rueda
class Rueda
{
public
$nombre;

function
__construct($nombre)
{
$this->nombre = $nombre;
}
}

//// una Moto es un Vehiculo
//// una Moto tiene Ruedas
class Moto extends Vehiculo
{
public
$marca;
public
$ruedas = array();

//// constructor de copia para Moto
//// $that referencia al objeto original
//// $this referencia al objeto clonado
function __clone()
{
$this->ruedas['del'] = new Rueda ($that->ruedas['del']->nombre);
$this->ruedas['tras'] = new Rueda ($that->ruedas['tras']->nombre);
}
}

 

El método __clone() de Moto se encarga de crear nuevas ruedas en la moto clonada. Como en el constructor de copia intervienen dos objetos, PHP proporciona la palabra clave $that, que hace referencia al objeto original, mientras que $this hace referencia al nuevo objeto (objeto clonado).

Hay que tener en cuenta que la copia bit a bit se sigue realizando de forma automática, replicando todos los atributos del objeto Moto original en el objeto Moto duplicado. La funcionalidad del método __clone() incluido en una clase se ejecuta después de esta copia bit a bit. En el ejemplo anterior, el atributo $marca se duplica de forma automática (el método __clone() no hace nada al respecto):

//// creamos dos ruedas
$rueda_delantera_mich = new Rueda ("delantera-michelin");
$rueda_trasera_mich = new Rueda ("trasera-michelin");


//// construimos una moto
$yamaha_R1 = new Moto();
$yamaha_R1->marca = "Yamaha R1";
$yamaha_R1->ruedas['del'] = $rueda_delantera_mich;
$yamaha_R1->ruedas['tras'] = $rueda_trasera_mich;

//// alehop! -> clonamos la moto
$yamaha_R1_bis = clone $yamaha_R1;


//// vemos que ruedas tiene cada moto
echo "Yamaha R1:";
echo
"<br>";
echo
"Marca: ".$yamaha_R1->marca;
echo
"<br>";
echo
"Delantera: ".$yamaha_R1->ruedas['del']->nombre;
echo
"<br>";
echo
"Trasera: ".$yamaha_R1->ruedas['tras']->nombre;
echo
"<br><br>";

//// Yamaha R1:
//// Marca: Yamaha R1
//// Delantera: delantera-michelin
//// Trasera: trasera-michelin



echo
"Yamaha R1 (bis):";
echo
"<br>";
echo
"Marca: ".$yamaha_R1->marca;
echo
"<br>";
echo
"Delantera: ".$yamaha_R1_bis->ruedas['del']->nombre;
echo
"<br>";
echo
"Trasera: ".$yamaha_R1_bis->ruedas['tras']->nombre;
echo
"<br><br>";

//// Yamaha R1 (bis):
//// Marca: Yamaha R1
//// Delantera: delantera-michelin
//// Trasera: trasera-michelin

Para comprobar que las dos motos no comparten las mismas ruedas:

//// ahora cambiamos las ruedas de la Yamaha R1
$yamaha_R1->ruedas['del']->nombre = "delantera-dunlop";
$yamaha_R1->ruedas['tras']->nombre = "trasera-dunlop";

//// y vemos las ruedas que tiene cada moto
echo "Yamaha R1:";
echo
"<br>";
echo
"Marca: ".$yamaha_R1->marca;
echo
"<br>";
echo
"Delantera: ".$yamaha_R1->ruedas['del']->nombre;
echo
"<br>";
echo
"Trasera: ".$yamaha_R1->ruedas['tras']->nombre;
echo
"<br><br>";

//// Yamaha R1:
//// Marca: Yamaha R1
//// Delantera: delantera-dunlop
//// Trasera: trasera-dunlop



echo
"Yamaha R1 (bis):";
echo
"<br>";
echo
"Marca: ".$yamaha_R1->marca;
echo
"<br>";
echo
"Delantera: ".$yamaha_R1_bis->ruedas['del']->nombre;
echo
"<br>";
echo
"Trasera: ".$yamaha_R1_bis->ruedas['tras']->nombre;
echo
"<br><br>";

//// Yamaha R1 (bis):
//// Marca: Yamaha R1
//// Delantera: delantera-michelin
//// Trasera: trasera-michelin

 

 

Felipe Fernández Perera : Google+