Avatar ·

Как сделать полную копию массива в PHP: оператор присваивания vs clone

Привет, коллеги! Я новичок в PHP и столкнулся с непонятной ситуацией. У меня есть массив с данными пользователей: ```php $users = [ ['name' => 'Иван', 'age' => 25], ['name' => 'Мария', 'age' => 30] ]; ``` Мне нужно создать его полную копию, чтобы изменять данные в копии, не затрагивая оригинал. Я попробовал простое присваивание: ```php $usersCopy = $users; $usersCopy[0]['name'] = 'Петр'; print_r($users[0]['name']); // Вывело "Петр", а я ожидал "Иван"! ``` Почему изменился оригинал? Я думал, что в PHP массивы копируются при присваивании. Пробовал использовать `clone`: ```php $usersCopy = clone $users; // Но clone работает только с объектами, выдаёт ошибку для массивов ``` И ещё я читал про `array_copy()`, но такой функции в PHP нет. Подскажите, как правильно создать независимую копию многомерного массива в PHP? Нужно, чтобы изменения в копии никак не влияли на оригинал. Спасибо! <<>>

Понимание копирования массивов в 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.