Копирование массива в PHP: простое присваивание или клон?

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Глубокое копирование массива в PHP: мифы и реальность

Представьте ситуацию: вы пишете скрипт, который обрабатывает данные из базы. Вы получаете массив пользователей, делаете с ним что-то, а затем внезапно обнаруживаете, что исходные данные изменились. Знакомо? Если вы работаете с PHP, эта проблема может застать врасплох даже опытного разработчика.

Вопрос «Is there a function to make a copy of a PHP array to another?» на Stack Overflow набрал десятки тысяч просмотров. И это не случайно. На первый взгляд, кажется, что копировать массивы в PHP — это тривиальная задача. Но дьявол, как всегда, кроется в деталях.

В этой статье мы разберём все возможные способы копирования массива в PHP: от простого присваивания до работы с многомерными структурами. Вы узнаете, когда PHP создаёт реальную копию, а когда — лишь ссылку на данные. Поехали!

Прямое присваивание: первый сюрприз

Самый интуитивный способ — просто присвоить массив другой переменной:

$original = ['apple', 'banana', 'cherry'];
$copy = $original;
$copy[0] = 'mango';

print_r($original);
// Array ( [0] => apple [1] => banana [2] => cherry )
print_r($copy);
// Array ( [0] => mango [1] => banana [2] => cherry )

Удивительно, но это работает! В PHP массивы — это value types (типы значений), а не ссылочные типы. Когда вы присваиваете массив другой переменной, PHP создаёт поверхностную копию (shallow copy) с отложенным копированием (copy-on-write).

Простыми словами: пока вы не меняете ни один из массивов, они указывают на одни и те же данные в памяти. Как только вы пытаетесь изменить элемент в одной из переменных, PHP выделяет новую память и копирует данные. Это очень эффективно и не требует лишних действий.

Вывод: для простых одномерных массивов прямое присваивание — это полноценное копирование.

Подводный камень: объекты внутри массива

А теперь представьте, что в массиве лежат объекты:

class User {
    public $name;
    public function __construct($name) {
        $this->name = $name;
    }
}

$original = [new User('Alice'), new User('Bob')];
$copy = $original;
$copy[0]->name = 'Charlie';

print_r($original[0]->name); // Charlie

Вот он, сюрприз! Поверхностная копия скопировала ссылку на объект, а не сам объект. Изменяя объект в копии, вы меняете его в оригинале. Для объектов нужен другой подход.

Функция array_merge(): неочевидные нюансы

Часто можно встретить совет использовать array_merge() для копирования массива:

$original = [1, 2, 3];
$copy = array_merge([], $original);
$copy[0] = 100;

print_r($original); // [1, 2, 3]

Да, это работает. Но есть важный нюанс: array_merge() сбрасывает числовые ключи и перенумеровывает их с нуля. Если ваши ключи — строки, то всё в порядке. А если числовые — будьте готовы к сюрпризам:

$original = ['a' => 1, 2 => 'two', 5 => 'five'];
$copy = array_merge([], $original);

print_r($copy);
// Array ( [a] => 1 [0] => two [1] => five )

Видите? Ключи 2 и 5 превратились в 0 и 1. Это может поломать логику, если порядок ключей важен.

Вывод: array_merge() подходит только для копирования массивов со строковыми ключами или когда переиндексация не страшна.

Копирование с помощью цикла: ручное управление

Самый надёжный, но и самый многословный способ — пройтись по массиву циклом:

$original = ['x' => 10, 'y' => 20];
$copy = [];

foreach ($original as $key => $value) {
    $copy[$key] = $value;
}

$copy['x'] = 100;
print_r($original['x']); // 10

Для простых массивов это избыточно. Но если вам нужно сделать глубокое копирование (deep copy), то без рекурсивного цикла не обойтись:

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;
}

$original = [
    'user' => new User('Alice'),
    'hobbies' => ['reading', 'coding']
];

$copy = deepCopy($original);
$copy['user']->name = 'Bob';

print_r($original['user']->name); // Alice — оригинал не изменился!

Этот подход даёт полный контроль, но требует написания кода. Для стандартных задач лучше использовать готовые решения.

Оператор spread (PHP 7.4+)

Начиная с PHP 7.4, можно использовать оператор распаковки ... для копирования массива:

$original = ['a', 'b', 'c'];
$copy = [...$original];
$copy[0] = 'z';

print_r($original); // ['a', 'b', 'c']

Это работает аналогично прямому присваиванию — создаётся поверхностная копия. Для объектов внутри массива действует то же правило: ссылки копируются, а не объекты.

Преимущество spread-оператора — лаконичность и читаемость. Недостаток — не работает с ассоциативными массивами, у которых есть строковые ключи (на самом деле работает, но если ключи повторяются — будет перезапись).

array_slice(): ещё один способ

Функция array_slice() без указания длины возвращает копию всего массива:

$original = [10, 20, 30];
$copy = array_slice($original, 0);
$copy[0] = 99;

print_r($original); // [10, 20, 30]

Этот метод тоже делает поверхностную копию. Он сохраняет числовые ключи, но для ассоциативных массивов со строковыми ключами поведение может отличаться.

Когда использовать: когда нужно скопировать часть массива или когда вы уже используете array_slice() для других целей.

Клонирование объектов: ключ к глубокой копии

Если ваш массив содержит объекты, и вы хотите скопировать их полностью, нужно использовать clone:

class User {
    public $name;
    public $data;
    public function __construct($name, $data = []) {
        $this->name = $name;
        $this->data = $data;
    }
    //                

Блоги

Рекомендаций по книгам