Массивы Массив в PHP — это упорядоченная структура данных, которая связывает значения и ключи. Этот тип данных оптимизирован для разных целей, поэтому с ним работают как с массивом, списком (вектором), хеш-таблицей (реализацией карты), словарём, коллекцией, стеком, очередью и, возможно, чем-то ещё. Поскольку значениями массива бывают другие массивы, также доступны деревья и многомерные массивы. Объяснение этих структур данных выходит за рамки этого руководства, но как минимум один пример будет приведён для каждой из них. За дополнительной информацией обращаются к большому объему литературы по этой обширной теме. Синтаксис Определение при помощи <function>array</function> Массив (array) создают языковой конструкцией array. В качестве аргументов она принимает любое количество разделённых запятыми пар ключ => значение. array( key => value, key2 => value2, key3 => value3, ... ) Запятая после последнего элемента массива необязательна и её можно опустить. Обычно это делается для однострочных массивов, — лучше предпочесть array(1, 2) вместо array(1, 2, ). Для многострочных массивов, наоборот, обычно указывают висящую запятую, так как упрощает добавление новых элементов в конец массива. Существует короткий синтаксис массива, который заменяет языковую конструкцию array() выражение []. Простой массив "bar", "bar" => "foo", ); // Работа с коротким синтаксисом массива $array = [ "foo" => "bar", "bar" => "foo", ]; ?> ]]> Ключ массива (key) разрешено указывать либо как целочисленное значение (int), либо как строку (string). Значение массива (value) может принадлежать любому типу данных. Дополнительно произойдут следующие преобразования ключа key: Строки (string), содержащие целое число (int) (исключая случаи, когда перед числом указывают знак +), будут преобразованы в целое число (int). Например, ключ со значением «8» сохранится со значением 8. При этом, значение «08» не преобразуется, так как оно — не корректное десятичное целое. Числа с плавающей точкой (float) также преобразуются в целочисленные значения (int) — дробная часть будет отброшена. Например, ключ со значением 8.7 будет сохранится со значением 8. Логический тип (bool) также преобразовывается в целочисленный тип (int). Например, ключ со значением &true; сохранится со значением 1, а ключ со значением &false; сохранится со значением 0. Тип null преобразуется в пустую строку. Например, ключ со значением null сохранится со значением "". Массивы (array) и объекты (object) нельзя указывать как ключи. Это сгенерирует предупреждение: Недопустимый тип смещения (Illegal offset type). Если нескольким элементам в объявлении массива указан одинаковый ключ, то только последний будет сохранён, а другие будут перезаписаны. Пример преобразования типов и перезаписи элементов "a", "1" => "b", 1.5 => "c", true => "d", ); var_dump($array); ?> ]]> &example.outputs; string(1) "d" } ]]> Поскольку все ключи в приведённом примере преобразуются к 1, значение будет перезаписано на каждый новый элемент и останется только последнее присвоенное значение — «d». PHP разрешает массивам содержать одновременно целочисленные (int) и строковые (string) ключи, поскольку PHP одинаково воспринимает индексные и ассоциативные массивы. Смешанные целочисленные (<type>int</type>) и строковые (<type>string</type>) ключи "bar", "bar" => "foo", 100 => -100, -100 => 100, ); var_dump($array); ?> ]]> &example.outputs; string(3) "bar" ["bar"]=> string(3) "foo" [100]=> int(-100) [-100]=> int(100) } ]]> Ключ (key) — необязателен. Если он не указан, PHP инкрементирует предыдущее наибольшее целочисленное (int) значение ключа. Индексные массивы без ключа ]]> &example.outputs; string(3) "foo" [1]=> string(3) "bar" [2]=> string(5) "hallo" [3]=> string(5) "world" } ]]> Разрешено указывать ключ одним элементам и пропускать для других: Ключи для некоторых элементов "c", "d", ); var_dump($array); ?> ]]> &example.outputs; string(1) "a" [1]=> string(1) "b" [6]=> string(1) "c" [7]=> string(1) "d" } ]]> Видно, что последнее значение «d» присвоилось ключу 7. Это произошло потому, что перед этим самым большим значением целочисленного ключа было 6. Расширенный пример преобразования типов и перезаписи элементов 'a', '1' => 'b', // значение «b» перезапишет значение «a» 1.5 => 'c', // значение «c» перезапишет значение «b» -1 => 'd', '01' => 'e', // поскольку это не целочисленная строка, она НЕ перезапишет ключ 1 '1.5' => 'f', // поскольку это не целочисленная строка, она НЕ перезапишет ключ 1 true => 'g', // значение «g» перезапишет значение «c» false => 'h', '' => 'i', null => 'j', // значение «j» перезапишет значение «i» 'k', // значение «k» присваивается ключу 2. Потому что самый большой целочисленный ключ до этого был 1 2 => 'l', // значение «l» перезапишет значение «k» ); var_dump($array); ?> ]]> &example.outputs; string(1) "g" [-1]=> string(1) "d" ["01"]=> string(1) "e" ["1.5"]=> string(1) "f" [0]=> string(1) "h" [""]=> string(1) "j" [2]=> string(1) "l" } ]]> Этот пример включает все вариации преобразования ключей и перезаписи элементов Доступ к элементам массива через синтаксис квадратных скобок Доступ к элементам массива разрешено получать, используя синтакс array[key]. Доступ к элементам массива "bar", 42 => 24, "multi" => array( "dimensional" => array( "array" => "foo" ) ) ); var_dump($array["foo"]); var_dump($array[42]); var_dump($array["multi"]["dimensional"]["array"]); ?> ]]> &example.outputs; До PHP 8.0.0 квадратные и фигурные скобки могли взаимозаменяться при доступе к элементам массива (например, в приведённом примере $array[42] и $array{42} делали одно и то же). Синтаксис фигурных скобок устарел с PHP 7.4.0 и больше не поддерживается с PHP 8.0.0. Разыменование массива ]]> Попытка доступа к неопределённому ключу в массиве — это то же самое, что и попытка доступа к любой другой неопределённой переменной: будет выдана ошибка уровня E_WARNING (ошибка уровня E_NOTICE до PHP 8.0.0), и результат будет равен &null;. Попытка разыменовать не массив, а скалярное значение, которое отличается от строки (string), отдаст &null;, тогда как разыменовывание строки (string) трактует её как индексный массив. При такой попытке до PHP 7.4.0 не выдавалось сообщение об ошибке. С PHP 7.4.0 выдаётся ошибка уровня E_NOTICE; с PHP 8.0.0 выдаётся ошибка уровня E_WARNING. Создание и модификация с применением синтаксиса квадратных скобок Разработчик может изменять существующий массив явной установкой значений. Это делается путём присвоения значений массиву (array) с указанием ключа в квадратных скобках. Кроме того, если опустить ключ, получится пустая пара скобок ([]). $arr[key] = value; $arr[] = value; // Ключ key может принадлежать типу int или string // Значение value может быть любым значением любого типа Если массив $arr ещё не существует или для него задано значение &null; или &false;, он будет создан. Таким образом, это ещё один способ определить массив array. Однако такой способ применять не рекомендовано, так как если переменная $arr уже содержит значение (например, строку (string) из переменной запроса), то это значение останется на месте и выражение [] может означать доступ к символу в строке. Лучше инициализировать переменную явным присваиванием значения. Начиная с PHP 7.1.0 оператор пустого индекса на строке выбросит фатальную ошибку. Раньше строка молча преобразовывалась в массив. С PHP 8.1.0 способ, которым создавали новый массив приведением к нему значения &false;, устарел. Способ, которым создают новый массив приведением к нему значения &null; и неопределённого значения, по-прежнему доступен. Для изменения конкретного значения элементу просто присваивают новое значение, указывая его ключ. Если нужно удалить пару ключ/значение, необходимо вызывать конструкцию unset. 1, 12 => 2); $arr[] = 56; // В этом месте скрипта это // то же самое, что и $arr[13] = 56; $arr["x"] = 42; // Это добавляет в массив новый // элемент с ключом «x» unset($arr[5]); // Это удаляет элемент из массива unset($arr); // Это удаляет весь массив ?> ]]> Как было сказано ранее, если разработчик не указал ключ, то будет взят максимальный из существующих целочисленных (int) индексов, и новым ключом будет это максимальное значение (в крайнем случае 0) плюс 1. Если целочисленных (int) индексов ещё нет, то ключом будет 0 (ноль). Учитывают, что максимальное целое значение ключа не обязательно существует в массиве в текущий момент. Оно могло существовать в массиве какое-то время с момента последней переиндексации. Следующий пример это иллюстрирует: $value) { unset($array[$i]); } print_r($array); // Добавляем элемент (обратите внимание, что новым ключом будет 5, а не 0). $array[] = 6; print_r($array); // Переиндексация: $array = array_values($array); $array[] = 7; print_r($array); ?> ]]> &example.outputs; 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 ) Array ( ) Array ( [5] => 6 ) Array ( [0] => 6 [1] => 7 ) ]]> Деструктуризация массива Массивы разрешено деструктурировать языковыми конструкциями [] (начиная с PHP 7.1.0) или list. Эти языковые конструкции разрешено использовать для деструктуризации массива на отдельные переменные. ]]> Деструктуризацию массива также выполняют в конструкции &foreach; для деструктуризации многомерного массива во время итерации по массиву. ]]> Элементы массива будут проигнорированы, если переменная не указана. Деструктуризация массива начинается с индекса 0. ]]> С PHP 7.1.0 ассоциативные массивы также разрешено деструктурировать. Это упрощает выбор нужного элемента в массивах с числовым индексом, так как индекс может быть указан явно. 1, 'bar' => 2, 'baz' => 3]; // Присваивание элемента с индексом «baz» переменной $three ['baz' => $three] = $source_array; echo $three; // выведет 3 $source_array = ['foo', 'bar', 'baz']; // Присваивание элемента с индексом 2 переменной $baz [2 => $baz] = $source_array; echo $baz; // выведет «baz» ?> ]]> Деструктуризацией массив пользуются, чтобы поменять две переменные местами. ]]> Оператор ... не поддерживается в присваиваниях. Попытка получить доступ к неопределённому ключу массива аналогична обращению к любой другой неопределённой переменной: будет выдано сообщение об ошибке уровня E_WARNING (ошибки уровня E_NOTICE до PHP 8.0.0), а результатом будет значение &null;. Полезные функции Для работы с массивами есть довольного много полезных функций. Подробнее об этом рассказано в разделе «Функции для работы с массивами». Языковая конструкция unset умеет удалять ключи массива. Обратите внимание, что массив НЕ будет переиндексирован. Если нужно поведение в стиле «удалить и сдвинуть», можно переиндексировать массив функцией array_values. 'один', 2 => 'два', 3 => 'три'); unset($a[2]); /* даст массив, представленный так: $a = array(1 => 'один', 3 => 'три'); а НЕ так: $a = array(1 => 'один', 2 => 'три'); */ $b = array_values($a); // Теперь $b это array(0 => 'один', 1 => 'три') ?> ]]> Управляющая конструкция &foreach; существует специально для массивов. Она предлагает простой способ перебора массива. Что можно и нельзя делать с массивами Почему выражение <literal>$foo[bar]</literal> неверно? Рекомендовано заключать в кавычки строковый литерал в индексе ассоциативного массива. Например, нужно писать $foo['bar'], а не $foo[bar]. Но почему? Часто в старых скриптах можно встретить следующий синтаксис: ]]> Это неверно, хотя и работает. Причина в том, что этот код содержит неопределённую константу (bar), а не строку ('bar' — обратите внимание на кавычки). Это работает, потому что PHP автоматически преобразовывает «голую строку» (не заключённую в кавычки строку, которая не соответствует ни одному из известных символов языка) в строку со значением этой «голой строки». Например, если константа с именем bar не определена, то PHP заменит bar на строку «bar» и будет работать с ней. Резервный вариант для обработки неопределённой константы как пустой строки выдаёт ошибку уровня E_NOTICE. Начиная с PHP 7.2.0 поведение объявлено устаревшим и выдаёт ошибку уровня E_WARNING. Начиная с PHP 8.0.0 удалено и выбрасывает исключение Error. Это не значит, что нужно всегда заключать ключ в кавычки. Не обязательно заключать в кавычки константы или переменные, поскольку это помешает PHP обрабатывать их. ]]> &example.outputs; Дополнительные примеры, которые подтверждают этот факт: 'apple', 'veggie' => 'carrot'); // Верно print $arr['fruit']; // apple print $arr['veggie']; // carrot // Неверно. Это работает, но из-за неопределённой константы с // именем fruit также выдаёт ошибку PHP уровня E_NOTICE // // Notice: Use of undefined constant fruit - assumed 'fruit' in... print $arr[fruit]; // apple // Давайте определим константу, чтобы продемонстрировать, что // происходит. Присвоим константе с именем fruit значение «veggie». define('fruit', 'veggie'); // Теперь обратите внимание на разницу print $arr['fruit']; // apple print $arr[fruit]; // carrot // Внутри строки это нормально. Внутри строк константы не // рассматриваются, так что ошибки E_NOTICE здесь не произойдёт print "Hello $arr[fruit]"; // Hello apple // С одним исключением: фигурные скобки вокруг массивов внутри // строк позволяют константам там находиться print "Hello {$arr[fruit]}"; // Hello carrot print "Hello {$arr['fruit']}"; // Hello apple // Это не будет работать и вызовет ошибку обработки: // Parse error: parse error, expecting T_STRING' or T_VARIABLE' or T_NUM_STRING' // Это, конечно, также действует и с суперглобальными переменными в строках print "Hello $arr['fruit']"; print "Hello $_GET['foo']"; // Ещё одна возможность — конкатенация print "Hello " . $arr['fruit']; // Hello apple ?> ]]> Если директива error_reporting настроена на режим отображения ошибок уровня E_NOTICE (например, E_ALL), ошибки сразу будут видны. По умолчанию директива error_reporting настроена на то, чтобы не показывать предупреждения. Как указано в разделе о синтаксисе, внутри квадратных скобок («[» и «]») должно быть выражение. То есть вот такой код работает: ]]> Это пример работы с возвращаемым функцией значением в качестве индекса массива. PHP также знает о константах: ]]> Обратите внимание, что E_ERROR — это такой же допустимый идентификатор, как и bar в первом примере. Но последний пример по существу эквивалентен такой записи: ]]> поскольку значение константы E_ERROR соответствует значению 1 и т. д. Так что же в этом плохого? Когда-нибудь в будущем команда разработчиков PHP, возможно, захочет добавить ещё одну константу или ключевое слово, либо константа из другого кода может вмешаться. Например, неправильно использовать слова empty и default, поскольку они относятся к зарезервированным ключевым словам. Повторим, внутри строки (string) в двойных кавычках допустимо не окружать индексы массива кавычками, поэтому «$foo[bar]» — допустимая запись. В примерах выше объяснено, почему, дополнительная информация дана в разделе об обработке переменных в строках. Преобразование в массив Преобразование целого числа (int), числа с плавающей точкой (float), строки (string), логического значения (bool) или ресурса (resource) в массив — создаёт массив с одним элементом с индексом 0 и значением скаляра, который был преобразован. Говоря по-другому, выражение (array) $scalarValue аналогично выражению array($scalarValue). Если объект (object) будет преобразован в массив, элементами массива будут свойства (переменные-члены) этого объекта. Ключами будут имена переменных-членов, со следующими примечательными исключениями: целочисленные свойства станут недоступны; к закрытым полям класса (private) в начало будет дописано имя класса; к защищённым полям класса (protected) в начало будет добавлен символ '*'. Эти добавленные с обоих сторон значения также получат NUL-байты. Неинициализированные типизированные свойства автоматически отбрасываются. {1} = null; } } var_export((array) new A()); ?> ]]> &example.outputs; NULL, '' . "\0" . '*' . "\0" . 'C' => NULL, 'D' => NULL, 1 => NULL, ) ]]> Это может вызвать несколько неожиданное поведение: ]]> &example.outputs; NULL ["AA"]=> NULL ["AA"]=> NULL } ]]> Приведённый код покажет 2 ключа с именем «AA», хотя один из них на самом деле имеет имя «\0A\0A». Если преобразовать в массив значение &null;, получится пустой массив. Сравнение Массивы сравнивают функцией array_diff и операторами массивов. Распаковка массива Массив, перед которым указан оператор ..., будет распакован во время определения массива. Только массивы и объекты, которые реализуют интерфейс Traversable, разрешено распаковывать. Распаковка массива оператором ... доступна начиная с PHP 7.4.0. Массив разрешено распаковывать несколько раз и добавлять обычные элементы до или после оператора ...: Простая распаковка массива 'd']; // ['a', 'b', 'c' => 'd'] ?> ]]> Распаковка массива оператором ... соблюдает семантику функции array_merge. То есть более поздние строковые ключи перезаписывают более ранние, а целочисленные ключи перенумеровываются: Распаковка массива с дублирующим ключом 1]; $arr2 = ["a" => 2]; $arr3 = ["a" => 0, ...$arr1, ...$arr2]; var_dump($arr3); // ["a" => 2] // целочисленный ключ $arr4 = [1, 2, 3]; $arr5 = [4, 5, 6]; $arr6 = [...$arr4, ...$arr5]; var_dump($arr6); // [1, 2, 3, 4, 5, 6] // Который [0 => 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5, 5 => 6] // где исходные целочисленные ключи не были сохранены. ?> ]]> Ключи, тип которых не принадлежит ни целыми числами, ни строками, выбрасывают исключение TypeError. Такие ключи генерируются только объектом Traversable. До PHP 8.1 распаковка массива со строковым ключом не поддерживалась: 4]; $arr3 = [...$arr1, ...$arr2]; // Fatal error: Uncaught Error: Cannot unpack array with string keys in example.php:5 $arr4 = [1, 2, 3]; $arr5 = [4, 5]; $arr6 = [...$arr4, ...$arr5]; // работает. [1, 2, 3, 4, 5] ?> ]]> Примеры Массив в PHP — гибкий тип данных. Вот несколько примеров: 'красный', 'taste' => 'сладкий', 'shape' => 'круг', 'name' => 'яблоко', 4 // ключом будет 0 ); $b = array('a', 'b', 'c'); // ...эквивалентен этому: $a = array(); $a['color'] = 'красный'; $a['taste'] = 'сладкий'; $a['shape'] = 'круг'; $a['name'] = 'яблоко'; $a[] = 4; // ключом будет 0 $b = array(); $b[] = 'a'; $b[] = 'b'; $b[] = 'c'; // после выполнения приведённого кода, переменная $a будет массивом // array('color' => 'красный', 'taste' => 'сладкий', 'shape' => 'круг', // 'name' => 'яблоко', 0 => 4), а переменная $b будет // array(0 => 'a', 1 => 'b', 2 => 'c'), или просто array('a', 'b', 'c'). ?> ]]> Вызов языковой конструкции array() 4, 'OS' => 'Linux', 'lang' => 'english', 'short_tags' => true ); // строго числовые ключи $array = array( 7, 8, 0, 156, -10 ); // это то же самое, что и array(0 => 7, 1 => 8, ...) $switching = array( 10, // ключ = 0 5 => 6, 3 => 7, 'a' => 4, 11, // ключ = 6 (максимальным числовым индексом было 5) '8' => 2, // ключ = 8 (число!) '02' => 77, // ключ = '02' 0 => 12 // значение 10 будет перезаписано на 12 ); // пустой массив $empty = array(); ?> ]]> Коллекция ]]> &example.outputs; Непосредственное изменение значений массива допустимо через передачу значений по ссылке. Изменение элемента в цикле ]]> &example.outputs; КРАСНЫЙ [1] => ГОЛУБОЙ [2] => ЗЕЛЁНЫЙ [3] => ЖЁЛТЫЙ ) ]]> Следующий пример создаёт массив, индексация которого начинается с единицы. Индекс, начинающийся с единицы 'Январь', 'Февраль', 'Март'); print_r($firstquarter); ?> ]]> &example.outputs; 'Январь' [2] => 'Февраль' [3] => 'Март' ) ]]> Заполнение массива ]]> Массивы упорядочены. Порядок изменяют разными функциями сортировки. Подробнее об этом рассказано в разделе «Функции для работы с массивами». Для подсчёта количества элементов в массиве вызывают функцию count. Сортировка массива ]]> Поскольку значению массива разрешено быть любым, значение может быть также другим массивом. Поэтому разрешено создавать рекурсивные и многомерные массивы. Рекурсивные и многомерные массивы array ( "a" => "апельсин", "b" => "банан", "c" => "яблоко" ), "numbers" => array ( 1, 2, 3, 4, 5, 6 ), "holes" => array ( "первая", 5 => "вторая", "третья" ) ); // Несколько примеров доступа к значениям предыдущего массива echo $fruits["holes"][5]; // напечатает «вторая» echo $fruits["fruits"]["a"]; // напечатает «апельсин» unset($fruits["holes"][0]); // удалит «первая» // Создаст новый многомерный массив $juices["apple"]["green"] = "хороший"; ?> ]]> Присваивание массива включает копирование значения. Чтобы скопировать массив по ссылке, указывают оператор присваивания по ссылке. ]]>