clone $obj створює новий об'єкт з тими самими значеннями властивостей. Але копія поверхнева: якщо властивість містить інший об'єкт, копіюється лише посилання на нього - обидва об'єкти ділять той самий вкладений об'єкт.
final class Order
{
public function __construct(public DateTime $createdAt, public array $items) {}
}
$a = new Order(new DateTime('2026-01-01'), ['book']);
$b = clone $a;
$b->items[] = 'pen'; // масив скопійовано: $a->items не змінився
$b->createdAt->modify('+1 day'); // об'єкт спільний: $a->createdAt теж змінився!
Масиви копіюються (за значенням), об'єкти всередині - ні.
__clone() викликається на копії після клонування - там роблять глибоку копію вкладених об'єктів:
public function __clone(): void
{
$this->createdAt = clone $this->createdAt;
}
Де це стає проблемою:
- «Незмінні» об'єкти з змінюваними частинами. Wither, що робить
clone $thisі змінює одне поле, ділить усі вкладені змінювані об'єкти з оригіналом. - Ресурси й з'єднання. Клон об'єкта з відкритим файлом чи з'єднанням з базою ділить той самий ресурс; закриття в одному ламає інший.
- Ідентичність: клон Eloquent-моделі зберігає той самий первинний ключ. Для копії запису в базі є
replicate().
Як уникнути проблем: робити вкладені значення незмінними (DateTimeImmutable, readonly value objects) - тоді поверхнева копія безпечна. З PHP 8.3 readonly-властивості можна переприсвоїти в __clone(), а PHP 8.5 додав clone($obj, ['prop' => $value]) для «зміненої копії» без ручного __clone.