Clonacion y Comparacion de Objetos
Los objetos en PHP se pasan por
referencia. Aprende a crear
copias independientes con clone
y a comparar objetos correctamente.
Objetos y referencias
Cuando asignas un objeto a otra variable, ambas apuntan al mismo objeto:
<?php
declare(strict_types=1);
class Producto
{
public string $nombre;
public float $precio;
public function __construct(string $nombre, float $precio)
{
$this->nombre = $nombre;
$this->precio = $precio;
}
}
$producto1 = new Producto('Laptop', 999);
$producto2 = $producto1; // NO es una copia, es la misma referencia
$producto2->precio = 899;
echo $producto1->precio; // 899 (cambio en ambos!)
echo $producto2->precio; // 899
Clonar objetos
Usa clone para crear una copia
independiente:
<?php
declare(strict_types=1);
class Producto
{
public string $nombre;
public float $precio;
public function __construct(string $nombre, float $precio)
{
$this->nombre = $nombre;
$this->precio = $precio;
}
}
$producto1 = new Producto('Laptop', 999);
$producto2 = clone $producto1; // Crea una copia independiente
$producto2->precio = 899;
$producto2->nombre = 'Laptop Gaming';
echo $producto1->precio; // 999 (sin cambios)
echo $producto1->nombre; // Laptop
echo $producto2->precio; // 899
echo $producto2->nombre; // Laptop Gaming
El método __clone
Puedes personalizar la clonacion implementando
__clone():
<?php
declare(strict_types=1);
class Documento
{
private string $id;
private string $titulo;
private string $creadoEn;
public function __construct(string $titulo)
{
$this->id = uniqid();
$this->titulo = $titulo;
$this->creadoEn = date('Y-m-d H:i:s');
}
public function __clone(): void
{
// Al clonar, generar nuevo ID y nueva fecha
$this->id = uniqid();
$this->creadoEn = date('Y-m-d H:i:s');
$this->titulo = $this->titulo . ' (copia)';
}
public function getId(): string
{
return $this->id;
}
public function getTitulo(): string
{
return $this->titulo;
}
}
$doc1 = new Documento('Informe anual');
sleep(1); // Esperar 1 segundo
$doc2 = clone $doc1;
echo $doc1->getId(); // 65a1b2c3...
echo $doc1->getTitulo(); // Informe anual
echo $doc2->getId(); // 65a1b2c4... (diferente)
echo $doc2->getTitulo(); // Informe anual (copia)
Por defecto, clone hace una
copia superficial. Si el objeto tiene
propiedades que son otros objetos, estos
no se clonan
automaticamente.
Clonacion profunda
<?php
declare(strict_types=1);
class Direccion
{
public string $ciudad;
public function __construct(string $ciudad)
{
$this->ciudad = $ciudad;
}
}
class Persona
{
public string $nombre;
public Direccion $direccion;
public function __construct(string $nombre, Direccion $direccion)
{
$this->nombre = $nombre;
$this->direccion = $direccion;
}
public function __clone(): void
{
// Clonar tambien los objetos internos
$this->direccion = clone $this->direccion;
}
}
$persona1 = new Persona('Ana', new Direccion('Madrid'));
$persona2 = clone $persona1;
$persona2->direccion->ciudad = 'Barcelona';
echo $persona1->direccion->ciudad; // Madrid (sin cambios)
echo $persona2->direccion->ciudad; // Barcelona
Comparar objetos
PHP tiene dos formas de comparar objetos:
<?php
declare(strict_types=1);
class Punto
{
public int $x;
public int $y;
public function __construct(int $x, int $y)
{
$this->x = $x;
$this->y = $y;
}
}
$a = new Punto(1, 2);
$b = new Punto(1, 2);
$c = $a;
// == compara valores de propiedades
var_dump($a == $b); // true (mismos valores)
var_dump($a == $c); // true
// === compara si son el MISMO objeto
var_dump($a === $b); // false (objetos diferentes)
var_dump($a === $c); // true (misma referencia)
// Con clone
$d = clone $a;
var_dump($a == $d); // true (mismos valores)
var_dump($a === $d); // false (objetos diferentes)
Ejemplo practico: Carrito de compras
<?php
declare(strict_types=1);
class ItemCarrito
{
public string $nombre;
public float $precio;
public int $cantidad;
public function __construct(string $nombre, float $precio, int $cantidad = 1)
{
$this->nombre = $nombre;
$this->precio = $precio;
$this->cantidad = $cantidad;
}
public function getSubtotal(): float
{
return $this->precio * $this->cantidad;
}
}
class Carrito
{
/** @var ItemCarrito[] */
private array $items = [];
public function agregar(ItemCarrito $item): void
{
$this->items[] = $item;
}
public function getTotal(): float
{
$total = 0;
foreach ($this->items as $item) {
$total += $item->getSubtotal();
}
return $total;
}
public function __clone(): void
{
// Clonar cada item para tener copia profunda
$itemsClonados = [];
foreach ($this->items as $item) {
$itemsClonados[] = clone $item;
}
$this->items = $itemsClonados;
}
}
$carrito = new Carrito();
$carrito->agregar(new ItemCarrito('Laptop', 999));
$carrito->agregar(new ItemCarrito('Mouse', 29, 2));
// Crear carrito de prueba
$carritoPrueba = clone $carrito;
// Modificar el carrito de prueba no afecta al original
Ejercicios
Ejercicio 1: Configuracion clonable
Crea una clase
Configuracion con un array
de opciones. Implementa
__clone() para que al
clonar, el array se copie correctamente
y no se comparta entre instancias.
Ver solucion
<?php
declare(strict_types=1);
class Configuracion
{
private array $opciones;
public function __construct(array $opciones = [])
{
$this->opciones = $opciones;
}
public function set(string $clave, mixed $valor): void
{
$this->opciones[$clave] = $valor;
}
public function get(string $clave): mixed
{
return $this->opciones[$clave] ?? null;
}
public function __clone(): void
{
// El array ya se copia por valor en PHP
// Pero si tuviera objetos dentro, habria que clonarlos
$this->opciones = $this->opciones;
}
}
$config1 = new Configuracion(['debug' => true, 'version' => '1.0']);
$config2 = clone $config1;
$config2->set('debug', false);
$config2->set('version', '2.0');
echo $config1->get('debug'); // true (sin cambios)
echo $config2->get('debug'); // false
echo $config1->get('version'); // 1.0
echo $config2->get('version'); // 2.0
Ejercicio 2: Historial de versiones
Crea una clase
Documento con
contenido y
version. Cada vez que se
clone, incrementa la version. Crea un
metodo para obtener una copia con
version incrementada.
Ver solucion
<?php
declare(strict_types=1);
class Documento
{
private string $titulo;
private string $contenido;
private int $version;
public function __construct(string $titulo, string $contenido)
{
$this->titulo = $titulo;
$this->contenido = $contenido;
$this->version = 1;
}
public function getVersion(): int
{
return $this->version;
}
public function getContenido(): string
{
return $this->contenido;
}
public function setContenido(string $contenido): void
{
$this->contenido = $contenido;
}
public function __clone(): void
{
$this->version++;
}
public function crearNuevaVersion(): self
{
return clone $this;
}
}
$doc1 = new Documento('Manual', 'Contenido inicial');
echo $doc1->getVersion(); // 1
$doc2 = $doc1->crearNuevaVersion();
$doc2->setContenido('Contenido actualizado');
echo $doc2->getVersion(); // 2
$doc3 = $doc2->crearNuevaVersion();
echo $doc3->getVersion(); // 3
Ejercicio 3: Comparador de usuarios
Crea una clase Usuario con
id, email y
nombre. Crea un metodo
esIgual(Usuario $otro): bool
que compare por email.
Ver solucion
<?php
declare(strict_types=1);
class Usuario
{
private int $id;
private string $email;
private string $nombre;
public function __construct(int $id, string $email, string $nombre)
{
$this->id = $id;
$this->email = strtolower($email);
$this->nombre = $nombre;
}
public function getId(): int
{
return $this->id;
}
public function getEmail(): string
{
return $this->email;
}
public function esIgual(Usuario $otro): bool
{
return $this->email === $otro->email;
}
public function esMismaInstancia(Usuario $otro): bool
{
return $this === $otro;
}
}
$usuario1 = new Usuario(1, 'Ana@Email.com', 'Ana Garcia');
$usuario2 = new Usuario(2, 'ana@email.com', 'Ana G.');
$usuario3 = new Usuario(3, 'luis@email.com', 'Luis');
var_dump($usuario1->esIgual($usuario2)); // true (mismo email)
var_dump($usuario1->esIgual($usuario3)); // false (diferente email)
$usuario4 = $usuario1;
var_dump($usuario1->esMismaInstancia($usuario4)); // true
var_dump($usuario1->esMismaInstancia($usuario2)); // false
¿Has encontrado un error o tienes una sugerencia para mejorar esta leccion?
Escribenos¿Te está gustando el curso?
Tenemos cursos premium con proyectos reales y soporte personalizado.
Descubrir cursos premium