W3docs

clone

Узнайте, как ключевое слово PHP clone копирует object, в чём разница между поверхностным и глубоким копированием, и как использовать магический метод __clone().

Ключевое слово PHP clone

В PHP объекты передаются по ссылке. Когда вы присваиваете одну переменную-object другой с помощью =, обе переменные указывают на один и тот же object — изменив один, вы измените и другой. Ключевое слово clone разрывает эту связь: оно создаёт совершенно новый object, который является копией существующего, поэтому вы можете изменять копию, не затрагивая оригинал.

На этой странице рассматриваются синтаксис clone, разница между поверхностным и глубоким копированием (главный источник ошибок при использовании clone), а также магический метод __clone(), позволяющий управлять процессом клонирования.

Если вы только начинаете работать с объектами, сначала прочитайте Классы и объекты PHP и Конструкторы.

Синтаксис

$copy = clone $original;

clone возвращает новый object. Оригинал остаётся нетронутым, а $copy является независимым экземпляром, свойства которого скопированы из $original.

Присваивание vs. clone

Именно для этого существует clone. При обычном присваивании обе переменные ссылаются на один 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 too

Используйте clone, чтобы получить по-настоящему отдельный object:

<?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; // 10

Простой пример

<?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 2022

Изменение $copy не затрагивает $original, поскольку они являются двумя отдельными объектами.

Поверхностное копирование: главный подводный камень

По умолчанию clone создаёт поверхностную копию. Скалярные свойства (string, int, boolean) копируются по значению, но если свойство содержит другой object, копируется только ссылка — и оригинал, и клон в итоге указывают на один и тот же вложенный object.

<?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 hp

Изменение двигателя клона также изменило двигатель оригинала — почти никогда это не то, чего вы хотите.

Глубокое копирование с помощью __clone()

Магический метод __clone() автоматически выполняется на новом объекте сразу после его дублирования. Используйте его для клонирования вложенных объектов, чтобы копия получила собственные независимые экземпляры (глубокое копирование):

<?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 hp

Теперь у двух автомобилей отдельные двигатели, поэтому изменение одного не влияет на другой. __clone() — подходящее место для любых исправлений при копировании: сброс автоматически сгенерированного ID, очистка кешированного значения или глубокое копирование вложенных объектов и массивов объектов.

Когда использовать clone

  • Рабочие копии / черновики — дублируйте object, чтобы пользователь мог редактировать копию, пока оригинал сохраняется (по аналогии с «Сохранить как»).
  • Паттерн «Прототип» — настройте один object и клонируйте его при необходимости получить новый преднастроенный экземпляр, вместо того чтобы каждый раз запускать затратный конструктор.
  • Вспомогательные средства неизменяемости — возвращайте изменённый клон вместо мутации $this, что является распространённым приёмом в объектах-значениях и DTO.

Прибегайте к clone, когда вам конкретно нужен второй независимый object; если вы лишь хотите читать один и тот же object из двух мест, вполне подойдёт обычная ссылка.

Важно помнить

  • clone по умолчанию создаёт поверхностную копию — вложенные объекты разделяются, а не дублируются.
  • Определите __clone() для выполнения глубокого копирования или любых других постобработочных действий.
  • __clone() выполняется на новом объекте, где $this — это клон.
  • clone предназначен для объектов. Массивы (array) в PHP уже копируются по значению при присваивании, поэтому им clone не нужен.

Практика

Практика
Что делает метод __clone() в PHP?
Что делает метод __clone() в PHP?
Was this page helpful?