Menu
Coddy logo textTech

Clonación de objetos a fondo

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 61 de 91.

Aprendiste sobre el método mágico __clone anteriormente en este curso. Ahora exploremos la diferencia crítica entre la clonación superficial y profunda cuando los objetos contienen otros objetos como propiedades.

Por defecto, PHP realiza una copia superficial (shallow copy) al clonar. Los valores escalares se duplican, pero las propiedades de objeto permanecen como referencias a los mismos objetos:

<?php
class Address {
    public function __construct(public string $city) {}
}

class Person {
    public function __construct(
        public string $name,
        public Address $address
    ) {}
}

$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;

$clone->name = "Bob";
$clone->address->city = "London";

echo $original->name . " - " . $original->address->city;

Salida:

Alice - London

Observa que cambiar $clone->name no afectó al original, ¡pero cambiar la ciudad de la dirección sí lo hizo! Ambos objetos comparten la misma instancia de Address.

Para crear una copia profunda donde los objetos anidados también se clonan, implemente __clone:

<?php
class Person {
    public function __construct(
        public string $name,
        public Address $address
    ) {}
    
    public function __clone(): void {
        $this->address = clone $this->address;
    }
}

$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;
$clone->address->city = "London";

echo $original->address->city;

Resultado:

Paris

Ahora cada Person tiene su propio Address independiente. Cuando tus objetos contienen múltiples objetos anidados, debes clonar cada uno en __clone para lograr una verdadera independencia entre el original y la copia.

challenge icon

Desafío

Fácil

Construyamos un sistema de gestión de documentos que demuestre la diferencia entre la clonación superficial (shallow) y la clonación profunda (deep). Crearás documentos que contienen información del autor y te asegurarás de que la clonación de un documento cree una copia verdaderamente independiente.

Organizarás tu código en tres archivos:

  • Author.php — Crea una clase Author que represente al autor de un documento. Usa la promoción de constructor para definir un $name (string) público y un $email (string) público.
  • Document.php — Crea una clase Document que contenga un autor. Incluye el archivo Author. La clase debe:
    • Usar la promoción de constructor para un $title (string) público y un $author (Author) público
    • Implementar el método mágico __clone() para realizar una clonación profunda — cuando se clone un documento, su autor también debe ser clonado para que los cambios en el autor del clon no afecten al original
    • Tener un método getInfo() que devuelva "[title] by [author name] ([author email])"
  • main.php — Incluye el archivo Document. Recibirás tres entradas: un título de documento, un nombre de autor y un correo electrónico de autor.

    Crea un Document con un Author usando las entradas proporcionadas. Clona el documento, luego modifica el nombre del autor del clon añadiéndole " (Copy)" y cambia el título del clon anteponiéndole "Copy of ".

    Imprime dos líneas:

    • La información del documento original
    • La información del documento clonado

    Si tu clonación profunda está implementada correctamente, el autor del documento original debe permanecer sin cambios incluso después de modificar al autor del clon.

Este desafío demuestra por qué es importante la clonación profunda — sin una implementación adecuada de __clone(), ambos documentos compartirían el mismo objeto Author, y cambiar uno afectaría al otro.

Hoja de referencia

Por defecto, PHP realiza una copia superficial (shallow copy) al clonar objetos. Los valores escalares se duplican, pero las propiedades de objeto permanecen como referencias a los mismos objetos.

Ejemplo de clonación superficial:

<?php
class Address {
    public function __construct(public string $city) {}
}

class Person {
    public function __construct(
        public string $name,
        public Address $address
    ) {}
}

$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;

$clone->name = "Bob";
$clone->address->city = "London";

echo $original->name . " - " . $original->address->city;
// Salida: Alice - London

Cambiar $clone->name no afecta al original, pero cambiar el objeto address anidado sí lo hace porque ambos objetos comparten la misma instancia de Address.

Para crear una copia profunda (deep copy) donde los objetos anidados también se clonen, implementa el método mágico __clone:

<?php
class Person {
    public function __construct(
        public string $name,
        public Address $address
    ) {}
    
    public function __clone(): void {
        $this->address = clone $this->address;
    }
}

$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;
$clone->address->city = "London";

echo $original->address->city;
// Salida: Paris

Cuando los objetos contienen múltiples objetos anidados, debes clonar cada uno en __clone para lograr una verdadera independencia entre el original y la copia.

Pruébalo tú mismo

<?php

require_once 'Document.php';

// Leer entradas
$title = trim(fgets(STDIN));
$authorName = trim(fgets(STDIN));
$authorEmail = trim(fgets(STDIN));

// TODO: Crear un Autor con el nombre y correo electrónico proporcionados

// TODO: Crear un Documento con el título proporcionado y el Autor

// TODO: Clonar el documento

// TODO: Modificar el nombre del autor del clon añadiéndole " (Copy)"

// TODO: Modificar el título del clon anteponiéndole "Copy of "

// TODO: Imprimir la información del documento original

// TODO: Imprimir la información del documento clonado

?>
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos