clone
Apprenez comment le mot-clé PHP clone copie les objets, pourquoi il fait une copie superficielle et comment utiliser la méthode magique __clone() pour contrôler les copies profondes.
Le mot-clé PHP clone
En PHP, les objets sont gérés par référence. Lorsque vous assignez une variable d'object à une autre avec =, les deux variables pointent vers le même object — modifiez l'un et l'autre change aussi. Le mot-clé clone rompt ce lien : il crée un tout nouvel object qui commence comme un duplicata d'un object existant, vous permettant ainsi de modifier la copie sans toucher à l'original.
Cette page couvre la syntaxe de clone, la différence entre une copie superficielle et une copie profonde (la principale source de bugs liés à clone), ainsi que la méthode magique __clone() qui vous permet de contrôler ce qui se passe pendant le clonage.
Si vous débutez avec les objets, lisez d'abord Classes et objets PHP et Constructeurs.
Syntaxe
$copy = clone $original;clone retourne un nouvel object. L'original reste intact, et $copy est une instance indépendante dont les propriétés sont copiées depuis $original.
Assignation vs. clone
C'est la raison d'être de clone. Avec une simple assignation, les deux variables font référence au même object :
<?php
class Counter
{
public int $value = 0;
}
$a = new Counter();
$b = $a; // same object, NOT a copy
$b->value = 10;
echo $a->value . PHP_EOL; // 10 — $a changed tooUtilisez clone pour obtenir un object véritablement séparé :
<?php
class Counter
{
public int $value = 0;
}
$a = new Counter();
$b = clone $a; // independent copy
$b->value = 10;
echo $a->value . PHP_EOL; // 0 — original is untouched
echo $b->value . PHP_EOL; // 10Un exemple de base
<?php
class Car
{
public string $make;
public string $model;
public int $year;
public function __construct(string $make, string $model, int $year)
{
$this->make = $make;
$this->model = $model;
$this->year = $year;
}
}
$original = new Car("Ford", "Mustang", 2022);
$copy = clone $original;
$copy->make = "Chevrolet";
$copy->model = "Corvette";
echo "Original: {$original->make} {$original->model} {$original->year}" . PHP_EOL;
echo "Copy: {$copy->make} {$copy->model} {$copy->year}" . PHP_EOL;
// Output:
// Original: Ford Mustang 2022
// Copy: Chevrolet Corvette 2022Modifier $copy laisse $original intact, car ce sont deux objets séparés.
Copie superficielle : le principal piège
Par défaut, clone effectue une copie superficielle. Les propriétés scalaires (strings, ints, booleans) sont copiées par valeur, mais si une propriété contient un autre object, seule la référence est copiée — l'original et le clone finissent par pointer vers le même object imbriqué.
<?php
class Engine
{
public function __construct(public int $horsepower) {}
}
class Car
{
public function __construct(public Engine $engine) {}
}
$original = new Car(new Engine(300));
$copy = clone $original;
// Both cars still share ONE Engine object
$copy->engine->horsepower = 500;
echo "Original engine: {$original->engine->horsepower} hp" . PHP_EOL;
echo "Copy engine: {$copy->engine->horsepower} hp" . PHP_EOL;
// Output:
// Original engine: 500 hp
// Copy engine: 500 hpModifier le moteur du clone a aussi modifié le moteur de l'original — ce qui n'est presque jamais ce que vous souhaitez.
Copie profonde avec __clone()
La méthode magique __clone() s'exécute automatiquement sur le nouvel object juste après sa duplication. Utilisez-la pour cloner tous les objets imbriqués afin que la copie dispose de ses propres instances indépendantes (une copie profonde) :
<?php
class Engine
{
public function __construct(public int $horsepower) {}
}
class Car
{
public function __construct(public Engine $engine) {}
public function __clone()
{
// Give the clone its own Engine instead of sharing the original's
$this->engine = clone $this->engine;
}
}
$original = new Car(new Engine(300));
$copy = clone $original;
$copy->engine->horsepower = 500;
echo "Original engine: {$original->engine->horsepower} hp" . PHP_EOL;
echo "Copy engine: {$copy->engine->horsepower} hp" . PHP_EOL;
// Output:
// Original engine: 300 hp
// Copy engine: 500 hpLes deux voitures ont maintenant des moteurs séparés, donc modifier l'un n'affecte pas l'autre. __clone() est l'endroit approprié pour toute correction par copie : réinitialiser un ID auto-généré, vider une valeur mise en cache, ou effectuer une copie profonde des objets imbriqués et des arrays d'objets.
Quand utiliser clone
- Copies de travail / brouillons — dupliquer un object pour qu'un utilisateur puisse modifier une copie pendant que l'original est préservé (pensez à « Enregistrer sous »).
- Patron prototype — pré-configurer un object et le
cloner chaque fois que vous avez besoin d'une nouvelle instance pré-paramétrée, plutôt que d'exécuter un constructeur coûteux à chaque fois. - Helpers d'immuabilité — retourner un clone modifié plutôt que de muter
$this, technique courante dans les value objects et les DTO.
Faites appel à clone lorsque vous avez spécifiquement besoin d'un second object indépendant ; si vous devez seulement lire le même object depuis deux endroits, une simple référence suffit.
À retenir
cloneeffectue une copie superficielle par défaut — les objets imbriqués sont partagés, pas dupliqués.- Définissez
__clone()pour effectuer une copie profonde ou tout autre ajustement par copie. __clone()s'exécute sur le nouvel object, où$thisest le clone.cloneest destiné aux objets. Les arrays en PHP sont déjà copiés par valeur lors de l'assignation, ils n'ont donc pas besoin declone.