Слияние массивов в PHP, почему лучше array_replace() а не array_merge()

Распространённая задача — сделать слияние значений нескольких массивов. Однако, в PHP это можно сделать разными способами и с разным конечным результатом. Почему-то большинству программистов первым на ум приходит способ с использованием функции array_merge(). Однако, ранее я уже писал о некоторых подводных камнях в её использовании совместно с http_build_query(). Следующий способ — использование функции array_replace(). Ну и последний вариант — сложение массивов с помощью оператора «+». Рассмотрим все три способа на примерах.

Все примеры будут приведены для PHP версии 7.1.8:

PHP 7.1.8 (cli) (built: Aug  1 2017 20:56:53) ( ZTS MSVC14 (Visual C++ 2015) x64 )

Допустим, есть изначальные массивы:

$a = ['', 'd' => true, 0 => 0, 2, 'a', 'b', null];
$b = [null, 'c' => 1, 'd' => false, 1 => 1, 0 => 1, 3];
dump(array_merge($a, $b) );
array:10 [
  0 => 0
  "d" => false
  1 => 2
  2 => "a"
  3 => "b"
  4 => null
  5 => 1
  "c" => 1
  6 => 1
  7 => 3
]
dump(array_replace($a, $b) );
array:7 [
  0 => 1
  "d" => false
  1 => 1
  2 => 3
  3 => "b"
  4 => null
  "c" => 1
]
dump($a + $b);
array:7 [
  0 => 0
  "d" => true
  1 => 2
  2 => "a"
  3 => "b"
  4 => null
  "c" => 1
]

Как видите, во всех способах получились разные итоговые массивы. Почему получается именно так? Для начала давайте посмотрим как PHP интерпретирует исходные массивы:

$a = ['', 'd' => true, 0 => 0, 2, 'a', 'b', null];
array:6 [
  0 => 0
  "d" => true
  1 => 2
  2 => "a"
  3 => "b"
  4 => null
]
$b = [null, 'c' => 1, 'd' => false, 1 => 1, 0 => 1, 3];
array:5 [
  0 => 1
  "c" => 1
  "d" => false
  1 => 1
  2 => 3
]

Во-первых, всем значениям без ключей присваиваются числовые ключи. Однако, присвоенный таким образом ключ может быть перезаписан следующим значением с объявленным равнозначным ключём. Но в данном случае это не влияет на конечный результат слияния массивов. Если вкратце, то array_merge затирает строковые ключи значениями из последующих массивов, но значения с числовыми ключами добавляет в конец массива и присваивает им новые ключи!

Оператор «+» и функция array_replace ведут себя более предсказуемо и отличаются только тем, что array_replace при слиянии берёт значение из самого последнего массива, а оператор «+» из самого первого. На мой взгляд, эти два способа более предпочтительны, чем использование array_merge(). Также обе функции имеют рекурсивные версии: array_merge_recursive() и array_replace_recursive(), но рассмотрены подробно они будут позже.

Теперь давайте заглянем в документацию.

Если входные массивы для array_merge() имеют одинаковые строковые ключи, тогда каждое последующее значение будет заменять предыдущее. Однако, если массивы имеют одинаковые числовые ключи, значение, упомянутое последним, не заменит исходное значение, а будет добавлено в конец массива.

array_replace() замещает значения первого массива значениями с такими же ключами из других переданных массивов. Если ключ из первого массива присутствует во втором массиве, его значение заменяется на значение из второго массива. Если ключ есть во втором массиве, но отсутствует в первом — он будет создан в первом массиве. Если ключ присутствует только в первом массиве, то сохранится как есть. Если для замены передано несколько массивов, они будут обработаны в порядке передачи и последующие массивы будут перезаписывать значения из предыдущих.

Оператор « возвращает левый массив, к которому был присоединён правый массив. Для ключей, которые существуют в обоих массивах, будут использованы значения из левого массива, а соответствующие им элементы из правого массива будут проигнорированы.