__clone e Clonagem de Objetos
Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy — lição 48 de 91.
Quando você atribui um objeto a outra variável no PHP, você não cria uma cópia - ambas as variáveis apontam para o mesmo objeto. Para criar uma cópia independente real, você precisa usar a palavra-chave clone:
<?php
class Product {
public function __construct(
public string $name,
public float $price
) {}
}
$original = new Product("Laptop", 999.99);
$copy = clone $original;
$copy->price = 899.99;
echo $original->price . " - " . $copy->price;
Saída:
999.99 - 899.99Por padrão, o PHP realiza uma cópia rasa (shallow copy) - ele copia todos os valores das propriedades diretamente. Isso funciona bem para valores escalares, mas objetos dentro do seu objeto ainda referenciam a mesma instância. O método mágico __clone() permite que você personalize este comportamento:
<?php
class Order {
public function __construct(
public int $id,
public array $items = []
) {}
public function __clone(): void {
$this->id = 0;
$this->items = [];
}
}
$order1 = new Order(101, ["Book", "Pen"]);
$order2 = clone $order1;
echo "Original: " . $order1->id . ", Clone: " . $order2->id;
Saída:
Original: 101, Clone: 0O método __clone() é chamado no novo objeto após a cópia rasa (shallow copy) ser feita. É aqui que você redefine IDs, cria novas cópias de objetos aninhados ou realiza qualquer limpeza necessária para que o clone seja verdadeiramente independente.
Desafio
FácilVamos construir um sistema de versionamento de documentos que demonstra como a clonagem cria cópias independentes e como o método mágico __clone() permite que você personalize o que acontece quando um objeto é clonado.
Você organizará seu código em dois arquivos:
Document.php— Crie uma classeDocumentque representa um documento versionado. Use a promoção de construtor para definir um$titlepúblico (string), um$contentpúblico (string) e um$versionpúblico (inteiro, com valor padrão 1). Adicione uma propriedade privada$createdAtque armazena o timestamp atual (usedate('Y-m-d H:i:s')no construtor). Seu documento precisa de:getCreatedAt()— retorna o timestamp de criação__clone()— quando um documento é clonado, a versão deve ser incrementada em 1, e o$createdAtdeve ser redefinido para o timestamp atual (o clone é uma nova versão criada "agora")
main.php— Inclua o arquivo Document. Você receberá três entradas: um título, um conteúdo e uma nova string de conteúdo para a versão clonada. Crie umDocumentcom o título e o conteúdo. Em seguida, clone-o para criar uma nova versão e atualize o conteúdo do clone para a nova string de conteúdo. Imprima quatro linhas:- A versão do documento original
- A versão do documento clonado
- O conteúdo do documento original
- O conteúdo do documento clonado
Este sistema mostra como o __clone() permite que você controle o que acontece durante a clonagem. O documento original mantém sua versão e hora de criação, enquanto o clone torna-se automaticamente uma nova versão com um novo timestamp — perfeito para rastrear o histórico de documentos onde cada clone representa uma nova revisão.
Folha de consulta
Para criar uma cópia independente de um objeto, use a palavra-chave clone:
<?php
class Product {
public function __construct(
public string $name,
public float $price
) {}
}
$original = new Product("Laptop", 999.99);
$copy = clone $original;
$copy->price = 899.99;
echo $original->price . " - " . $copy->price;
// Saída: 999.99 - 899.99
Por padrão, o PHP realiza uma shallow copy (cópia rasa) de todos os valores das propriedades. Para personalizar o comportamento de clonagem, use o método mágico __clone():
<?php
class Order {
public function __construct(
public int $id,
public array $items = []
) {}
public function __clone(): void {
$this->id = 0;
$this->items = [];
}
}
$order1 = new Order(101, ["Book", "Pen"]);
$order2 = clone $order1;
echo "Original: " . $order1->id . ", Clone: " . $order2->id;
// Saída: Original: 101, Clone: 0
O método __clone() é chamado no novo objeto após a cópia rasa ser feita, permitindo que você redefina propriedades ou crie cópias independentes de objetos aninhados.
Experimente você mesmo
<?php
require_once 'Document.php';
// Ler entradas
$title = trim(fgets(STDIN));
$content = trim(fgets(STDIN));
$newContent = trim(fgets(STDIN));
// TODO: Criar um Document com o título e o conteúdo
// TODO: Clonar o documento para criar uma nova versão
// TODO: Atualizar o conteúdo do clone para a nova string de conteúdo
// TODO: Imprimir quatro linhas:
// 1. A versão do documento original
// 2. A versão do documento clonado
// 3. O conteúdo do documento original
// 4. O conteúdo do documento clonado
?>Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave $thisMétodosPropriedadesConstrutor (__construct)Destrutor (__destruct)Recapitulação - Calculadora Simples4Herança
Herança BásicaA Palavra-chave parent::Sobrescrita de MétodoA Palavra-chave finalClasses AbstratasRecapitulação - Hierarquia de Funcionários7Encapsulamento
Público, Protegido, PrivadoModificadores de Acesso em DetalhesGetters e SettersOcultação de InformaçãoPromoção de Construtor (8.0)Recapitulação - Sistema de Registro de Alunos10Conceitos Avançados de POO
Composição vs HerançaInjeção de DependênciaClasses AnônimasEnums (PHP 8.1)Fibers (PHP 8.1)Mergulho Profundo em Clonagem de ObjetosGeradores e Iteradores13Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository2Namespaces e Autoloading
Introdução a NamespacesA Palavra-chave usePadrão de Autoloading PSR-4Autoloader do ComposerRecapitulação - Projeto Organizado5Interfaces e Contratos
Introdução a InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesInterface vs Classe AbstrataType Hinting com InterfacesRecapitulação - Calculadora de Formas8Métodos Mágicos
Introdução aos Métodos Mágicos__toString e __debugInfo__get, __set, __isset, __unset__call e __callStatic__clone e Clonagem de Objetos__serialize e __unserializeRecapitulação - Coleção Personalizada11Sistema de Tipos e Tratamento de Erros
Declarações de TiposTipos AnuláveisTipos de União e InterseçãoClasses de ExceçãoHierarquia de Exceções PersonalizadasTry, Catch, FinallyRecapitulação - Validador de Formulário14Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário3Propriedades de Classe
Propriedades de Instância vs EstáticasConstantes em ClassesMétodos e Propriedades EstáticosPropriedades Privadas e ProtegidasPropriedades Readonly (PHP 8.1)Recapitulação - Gerenciador de Conta Bancária6Polimorfismo
Sobrescrita de Métodos RevisitadaPolimorfismo via InterfacesType Hinting e Union TypesLate Static BindingRecapitulação - Processador de Pagamentos9Traits
Introdução a TraitsUsando Múltiplos TraitsResolução de Conflitos de TraitsMétodos Abstratos em TraitsTraits vs Herança12Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy