2.9 Ассоциативные массивы

Картинки по запросу PHP массивы

Возможно, вы уже догадались, что ассоциативные массивы — один из самых мощных инструментов в PHP. Массивы довольно часто реализуются в скриптовых языках. Давайте рассмотрим подробнее, как с ними работать. Массивы — это своеобразные контейнеры-переменные для хранения сразу нескольких величин, к которым можно затем быстро и удобно обратиться.
Пусть в программе необходимо описать список из нескольких имен. Можно сделать это так:
$namesList[0] = “Yuen Wo Ping”;
$namesList[1] = “Geofrey Darrow”;
$namesList[2] = “Hugo Weaving”;

Таким образом, мы по одному добавляем в массив $namesList элементы, например пронумерованные, начиная с 0. PHP узнает, что мы хотим создать массив по квадратным скобкам (нужно заметить, что для этого переменная $namesList в начале не должна еще быть инициализирована). Мы будем в дальнейшем называть массивы, ключи (или, как их часто называют, индексы — то, что стоит в квадратных скобках), которые нумеруются с нуля и идут без пропусков (а это далеко не всегда так, как мы вскоре увидим), списками.
Некоторые стандартные функции PHP, обрабатывающие массивы, требуют передавать в их параметрах именно списки, хотя чаще всего можно это ограничение обойти, передав им любой другой массив. В таком случае они все равно рассматривают массив как обычный список, т. е. не обращают никакого внимания на его ключи. Во многих ситуациях это бывает нежелательно, на чем мы чуть позже остановимся подробнее. Давайте теперь посмотрим, как можно распечатать наш список
<?php ## Демонстрация работы со списками
$namesList[0] = “Yuen Wo Ping”;
$namesList[1] = “Geofrey Darrow”;
$namesList[2] = “Hugo Weaving”;
echo “А вот первый элемент массива: “.$namesList[0].”<hr />”;
// Печатаем в цикле все элементы массива
for($i = 0; $i < count($namesList); $i++)
echo $namesList[$i].”<br />”;
?>
Как видите, самый простой способ — воспользоваться циклом for. Количество элементов в массиве легко можно определить, используя функцию count() или ее синоним sizeof().

Создание массива “на лету”. Автомассивы
В примере из листинга 10.1, казалось бы, все гладко. За исключением одного небольшого недостатка: каждый раз, добавляя имя, мы должны были выбирать для него номер и заботиться, чтобы ненароком не указать уже существующий. Чтобы этого избежать, можно написать те же команды так:
$namesList[] = “Yuen Wo Ping”;
$namesList[] = “Geofrey Darrow”;
$namesList[] = “Hugo Weaving”;
В этом случае PHP сам начнет (конечно, если переменная $namesList еще не существует) нумерацию с нуля и каждый раз будет прибавлять к счетчику по единице, создавая список. Согласитесь, довольно удобно. Разумеется, можно использовать скобки [] и не только в таком простом контексте, очень часто они применяются для более общего действия — добавления элемента в конец массива, например:
unset($FNames); // на всякий случай стираем массив
while ($f = очередное_имя_файла_в_текущем каталоге)
if (расширение_$f_есть_txt) $FNames[] = $f;
// теперь $FNames содержит список файлов с расширением txt
Если же нам нужно создать ассоциативный массив (мы будем его иногда называть хэшем), все делается совершенно аналогично, только вместо цифровых ключей мы должны указывать строковые. При этом следует помнить, что в строковых ключах буквы нижнего и верхнего регистров считаются различными. И еще: ключом может быть абсолютно любая строка, содержащая пробелы, символы перевода строки, нулевые символы и т. д. То есть, никаких ограничений на ключи не накладывается.
Поясним сказанное на примере. Пусть нам надо написать сценарий, который работает, как записная книжка: по фамилии абонента он выдает его имя. Мы можем организовать базу данных этой книжки в виде ассоциативного массива с ключами — фамилиями и соответствующими им значениями имен людей:
$names[“Anderson”] = “Thomas”;
$names[“Weaving”] = “Hugo”;
$names[“Darrow”] = “Geofrey”;
Далее, мы можем распечатать имя любого абонента командой:
$f = “Anderson”;
echo $names[“Weaving”] . ” said: Hmmm, mr. ” . $names[$f].”…”;
Как видите, тут никаких особенностей нет, все работает совершенно аналогично спискам, только с нецифровыми ключами. Возможно, вы скажете, что это не совсем так: например, нельзя воспользоваться циклом for, как мы это делали раньше, для вывода всех персоналий, и окажетесь правы. Вскоре мы рассмотрим три приема, с помощью  которых можно перебрать все элементы массива. Вы, скорее всего, будете применять их даже и для списков — настолько они удобны и универсальны, а к тому же и работают быстрее, чем последовательный перебор в цикле for с использованием $i.

Конструкция list()
Пусть у нас есть некоторый массив-список $list с тремя элементами: имя человека, его фамилия и возраст. Нам бы хотелось присвоить переменным $name, $surname и $age эти величины. Это, конечно, можно сделать так:
$name = $list[0];
$surname = $list[1];
$age = $list[2];
Но гораздо изящнее будет воспользоваться конструкцией list(), предназначенной как раз для таких целей:
list ($name, $surname, $age) = $list;
Согласитесь, выглядит несколько приятнее. Конечно, list() можно использовать для любого количества переменных: если в массиве не хватит элементов, чтобы их заполнить, им просто присвоятся неопределенные значения.
Что, если нам нужны только второй и третий элементы массива $list? В этом случае имеет смысл пропустить первый параметр в операторе list() вот так:
list (, $surname, $age) = $list;
Таким образом, мы получаем в переменных $surname и $age фамилию и возраст человека, не обращая внимания на его имя в первом аргументе.

ЗАМЕЧАНИЕ
Разумеется, можно пропускать любое количество элементов, как слева или справа, так и посередине списка. Главное — не забыть проставить нужное количество запятых.

Списки и ассоциативные массивы: путаница?..
Следует сказать несколько слов насчет ассоциативных массивов языка PHP. Во-первых, на самом деле все “остальные” массивы также являются ассоциативными (в частности, списки — тоже). Во-вторых, ассоциативные массивы в PHP являются направленными, т. е. в них существует определенный (и предсказуемый) порядок элементов, не зависящий от реализации. А значит, есть первый и последний элементы, и для каждого элемента можно определить следующий за ним. Именно по этой причине нам не нравится название “хэш” (в буквальном переводе — “мешанина”), хотя, конечно, в реализации PHP наверняка используются алгоритмы хэширования для увеличения быстродействия. Операция [] всегда добавляет элемент в конец массива, присваивая ему при этом такой числовой индекс, который бы не конфликтовал с уже имеющимися в массиве (точнее, выбирается номер, превосходящий все имеющиеся цифровые ключи в массиве). Вообще говоря, любая операция $array[ключ]=значение всегда добавляет элемент в конец
массива, конечно, за исключением тех случаев, когда ключ уже присутствует в массиве. Если вы захотите изменить порядок следования элементов в ассоциативном массиве, не изменяя в то же время их ключей, это можно сделать одним из двух способов: воспользоваться функциями сортировки или создать новый пустой массив и заполнить его в нужном порядке, перебрав элементы исходного массива.

Конструкция array() и многомерные массивы
Вернемся к предыдущему примеру. Нам необходимо написать программу, которая по фамилии некоторого человека из группы будет выдавать его имя. Поступим так же, как и раньше: будем хранить данные в ассоциативном массиве (сразу отбрасывая возможность составить ее из огромного числа конструкций if-else как неинтересную):
$names[“Weaving”] = “Hugo”;
$names[“Chong”] = “Marcus”;
Теперь можно, как мы знаем, написать:
echo $names[“Weaving”]; // выведет Hugo
echo $names[“ложка”]; // ошибка: в массиве нет такого элемента!
Идем дальше. Прежде всего, обратим внимание: приведенным выше механизмом мы никак не смогли бы создать пустой массив. Однако он очень часто может нам понадобиться, например, если мы не знаем, что раньше было в массиве $names, но хотим его проинициализировать указанным путем. Кроме того, каждый раз задавать массив указанным выше образом не очень-то удобно — приходится все время однообразно повторять строку $names…
Так вот, существует и второй способ создания массивов, выглядящий значительно компактнее. Мы уже упоминали его несколько раз — это использование ключевого слова array(). Например:
// Создает пустой массив $names
$names = array();
// Создает такой же массив, как в предыдущем примере с именами
$names = array(“Weaving” => “Hugo”, “Chong” => “Marcus”);
// Создает список с именами (нумерация 0, 1, 2)
$namesList = array(“Yuen Wo Ping”, “Geofrey Darrow”, “Hugo Weaving”);
Начиная с версии PHP 5.4, создать пустой массив можно при помощи пары квадратных скобок; приведенный выше пример в новом формате может выглядеть следующим образом:
// Создает пустой массив $names
$names = [];
// Создает такой же массив, как в предыдущем примере с именами
$names = [“Weaving” => “Hugo”, “Chong” => “Marcus”];
// Создает список с именами (нумерация 0, 1, 2)
$namesList = [“Yuen Wo Ping”, “Geofrey Darrow”, “Hugo Weaving”];
Новый синтаксис гораздо компактнее, а самое главное он повторяет синтаксис массивов в других современных скриптовых языках, таких как Python или Ruby.
Теперь займемся вопросом формирования двумерных (и вообще многомерных) массивов. Это довольно просто. В самом деле, мы уже говорили, что значениями переменных (и значениями элементов массива тоже, поскольку PHP не делает никаких различий между переменными и элементами массива) может быть все, что угодно, в частности — опять же массив. Так можно создавать ассоциативные массивы (а можно — списки) с любым числом измерений. Например, если кроме имени о человеке известен также его возраст, то можно инициировать массив $names так:
$dossier[“Anderson”] = [“name” => “Thomas”, “born” => “1962-03-11”];
$dossier[“Reeves”] = [“name” => “Keanu”, “born” => “1962-09-02”];
или даже так:
$dossier = [
“Anderson” => [“name” => “Thomas”, “born” => “1962-03-11”],
“Reeves” => [“name” => “Keanu”, “born” => “1962-09-02”],
];
Как же добраться до нужного элемента в нашем массиве? Нетрудно догадаться по аналогии с другими языками:
echo $dossier[“Anderson”][“name”]; // Thomas
echo $dossier[“Reeves”][“diff”]; // ошибка: нет элемента “diff”
Кстати, мы можем видеть, что ассоциативные массивы в PHP удобно использовать как некие структуры, хранящие данные. Это похоже на конструкцию struct в языке C (или record в Pascal).

Массивы-константы
Начиная с PHP 7, допускается создание констант-массивов, которые объявляются как самые обычные константы при помощи ключевого слова define:
define(
‘DOSSIER’,
[
“Anderson” => [“name” => “Thomas”, “born” => “1962-03-11”],
“Reeves” => [“name” => “Keanu”, “born” => “1962-09-02”],
]);
echo DOSSIER[“Anderson”][“name”]; // Thomas

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

Доступ по ключу
Как мы уже знаем, ассоциативные массивы — объекты, которые наиболее приспособлены для выборки из них данных путем указания нужного ключа. В PHP и для всех массивов, и для списков (которые, еще раз напомним, также являются массивами) используется один и тот же синтаксис, что является очень большим достоинством. Вот как это выглядит:
echo $names[“Weaving”]; // выводит элемент массива с ключом “Weaving”
echo $dossier[“Anderson”][“name”]; // так используются двумерные массивы
echo SomeFuncThatReturnsArray()[5]; // можно начиная с PHP 5.4
// До PHP 5.4
$result = SomeFuncThatReturnsArray();
echo $result[5];
Величина $array[ключ] является полноценным “левым значением”, т. е. может стоять в левой части оператора присваивания, от нее можно брать ссылку с помощью оператора &, и т. д. Например:
$names[“Davis”] = “Don”; // присваиваем элементу массива строку “Don”
$ref = &$dossier[“Reeves”][“name”]; // $ref – синоним элемента массива
$namesList[] = “Paul Doyle”; // добавляем новый элемент

Функция count()
Мы можем определить размер (количество элементов) в массиве при помощи стандартной функции count():
$num = count($namesList); // в $num количество элементов массива
Сразу отметим, что функция count() работает не только с массивами, но и с объектами и даже с обычными переменными (для последних результат выполнения count() всегда равен 1, как будто переменная — это массив с одним элементом). Впрочем, ее очень редко применяют для чего-либо, отличного от массива — разве что по ошибке.

Слияние массивов
Еще одна интересная операция — слияние массивов, т. е. создание массива, содержащего как элементы одного, так и другого массива. Реализуется это при помощи оператора +. Например:
$good = [“Arahanga”=>”Julian “, “Doran”=>”Matt”];
$bad = [“Goddard”=>”Paul”, “Taylor”=>”Robert”];
$all = $good + $bad;
В результате в $all окажется ассоциативный массив, содержащий все 4 элемента, причем порядок следования элементов будет зависеть от порядка, в котором массивы сливаются. Видите, как проявляется направленность массивов? Она заставляет оператор + стать некоммутативным, т. е. $good + $bad не равно $bad + $good.

Цикл foreach
Данный тип цикла предназначен специально для перебора всех элементов массива. Напоминаем, что массив — это набор так называемых ключей, каждому из которых соответствует некоторое значение. Выглядит он следующим образом:
foreach (массив as $ключ => $значение)
команды;
Здесь команды циклически выполняются для каждого элемента массива, при этом очередная пара ключ=>значение оказывается в переменных $ключ и $значение. Давайте рассмотрим пример (листинг 9.5), где покажем, как мы можем отобразить содержимое всех переменных окружения при помощи цикла foreach.
Вывод всех переменных окружения:
<?php ## Вывод всех переменных окружения
foreach($_SERVER as $k => $v)
echo “<b>$k</b> => <tt>$v</tt><br />\n”;
?>
Результат:
HTTP_HOST => boock
HTTP_CONNECTION => keep-alive
HTTP_UPGRADE_INSECURE_REQUESTS => 1
HTTP_USER_AGENT => Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
HTTP_ACCEPT => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
HTTP_ACCEPT_ENCODING => gzip, deflate
HTTP_ACCEPT_LANGUAGE => ru,en-US;q=0.8,en;q=0.6
HTTP_COOKIE => count=4
PATH => e:\ospanel\modules\php\PHP-5.6\ext;e:\ospanel\modules\php\PHP-5.6\pear;e:\ospanel\modules\php\PHP-5.6\pear\bin;e:\ospanel\modules\php\PHP-5.6;e:\ospanel\modules\imagemagick;e:\ospanel\modules\wget\bin;e:\ospanel\modules\git;e:\ospanel\modules\git\bin;e:\ospanel\modules\git\mingw\bin;e:\ospanel\modules\git\cmd;e:\ospanel\modules\database\MySQL-5.6\bin;e:\ospanel\modules\http\Apache-2.4\bin;e:\ospanel\modules\http\Apache-2.4;C:\Windows\system32;C:\Windows;C:\Windows\system32\Wbem;C:\Windows\SysWOW64
SystemRoot => C:\Windows
и т.д.

У цикла foreach имеется и другая форма записи, которую следует применять, когда нас не интересует значение ключа очередного элемента. Выглядит она так:
foreach ($массив as $значение)
команды;
Результат:
HTTP_HOST
HTTP_CONNECTION
HTTP_CACHE_CONTROL
HTTP_UPGRADE_INSECURE_REQUESTS
HTTP_USER_AGENT
HTTP_ACCEPT
HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE
HTTP_COOKIE
PATH
SystemRoot
COMSPEC
PATHEXT
WINDIR

Цикл foreach в форме, рассмотренной выше, оперирует не исходным массивом, а его копией. Это означает, что любые изменения, которые вносятся в массив, не могут быть “видны” из тела цикла. Такое поведение позволяет, например, в качестве массива использовать не только переменную, но и результат работы какой-нибудь функции, возвращающей массив. (В последнем случае функция будет вызвана всего один раз — до начала цикла, а затем работа станет производиться с копией возвращенного значения.) Для того чтобы иметь возможность изменять массив изнутри тела цикла, в PHP можно использовать ссылочный синтаксис:
foreaсh ($массив as $ключ=>&$значение) {
// здесь можно изменять $значение, при этом изменяются элементы
// исходного массива $массив
}

Залишити відповідь