Как сделать полную копию массива в PHP: оператор присваивания vs clone
Понимание копирования массивов в PHP
Вы столкнулись с классической особенностью PHP: простые (одномерные) массивы действительно копируются при присваивании благодаря механизму copy-on-write. Однако для многомерных массивов, содержащих объекты или вложенные массивы, поверхностное копирование не работает — копируются только ссылки на вложенные структуры.
Решение: глубокое копирование через unserialize/serialize
Самый надёжный способ создать полную независимую копию массива любой вложенности — использовать пару функций serialize() и unserialize():
<?php
$users = [
['name' => 'Иван', 'age' => 25, 'hobbies' => ['рыбалка', 'чтение']],
['name' => 'Мария', 'age' => 30, 'hobbies' => ['танцы', 'рисование']]
];
// Полное глубокое копирование
$usersCopy = unserialize(serialize($users));
// Теперь изменения в копии не затрагивают оригинал
$usersCopy[0]['name'] = 'Петр';
$usersCopy[0]['hobbies'][] = 'программирование';
echo $users[0]['name']; // Иван — оригинал не изменился
echo $users[0]['hobbies'][2]; // Notice: undefined offset — нет нового хобби
?>
Почему это работает?
serialize()преобразует массив в строковое представление со всей вложенной структуройunserialize()восстанавливает из строки совершенно новый массив, не связанный с оригиналом- Это создаёт глубокую копию (deep copy) любой сложности
Альтернативы для разных случаев
1. Для простых одномерных массивов — обычное присваивание работает отлично:
$simple = [1, 2, 3];
$simpleCopy = $simple;
$simpleCopy[0] = 100;
echo $simple[0]; // 1 — не изменился
2. Для массивов с объектами — нужно клонировать каждый объект вручную или использовать serialize/unserialize:
class User {
public $name;
public function __construct($name) { $this->name = $name; }
}
$objArray = [new User('Иван'), new User('Мария')];
$deepCopy = unserialize(serialize($objArray));
$deepCopy[0]->name = 'Петр';
echo $objArray[0]->name; // Иван — объект не изменился
3. Через рекурсивную функцию (для учебных целей):
function deepCopy($array) {
$result = [];
foreach ($array as $key => $value) {
if (is_array($value)) {
$result[$key] = deepCopy($value);
} elseif (is_object($value)) {
$result[$key] = clone $value;
} else {
$result[$key] = $value;
}
}
return $result;
}
Важные замечания
serialize/unserializeработает медленнее, чем ручное копирование, но для большинства задач это не критично- Метод не подходит, если в массиве есть ресурсы (например, файловые дескрипторы) или объекты с циклическими ссылками
- Для массивов с объектами, использующими
__sleep()и__wakeup(), нужно учитывать их логику
В вашем случае с массивом пользователей serialize/unserialize — идеальное решение. Оно гарантирует полную независимость копии от оригинала.
Использование array_map для копирования многомерных массивов
Если вам не нравится подход с сериализацией (например, из-за производительности или особенностей данных), можно использовать array_map() в сочетании с рекурсией:
<?php
$users = [
['name' => 'Иван', 'age' => 25],
['name' => 'Мария', 'age' => 30]
];
$usersCopy = array_map(function($item) {
return is_array($item) ? array_map('array_map', $item) : $item;
}, $users);
?>
Однако этот метод работает только для двухуровневых массивов. Для произвольной вложенности лучше использовать рекурсивную версию:
function arrayDeepCopy($array) {
return array_map(function($item) {
return is_array($item) ? arrayDeepCopy($item) : $item;
}, $array);
}
$usersCopy = arrayDeepCopy($users);
$usersCopy[0]['name'] = 'Петр';
echo $users[0]['name']; // Иван — оригинал не тронут
Когда это полезно
- Когда нужно обработать каждый элемент при копировании (например, преобразовать данные)
- Когда массив содержит только скалярные значения и вложенные массивы (без объектов)
- Для лучшей читаемости кода в команде (не все любят serialize)
Недостаток: функция не обрабатывает объекты — для них нужно добавлять проверку is_object() и вызов clone.
Ответов пока нет.
Войдите чтобы оставить ответ