Веб-программирование на PHP. (Часть V)

Руслан Курепин
http://kurepin.ru/main.phtml

Веб-программирование на PHP (Часть IV)

42. MySQL: Коррекция таблиц, дамп/загрузка данных
43. MySQL: Использование индексов
44. Сборка Apache+PHP+GD+MySQL
45. Работа с массивами. Начало
46. Работа с массивами. Продолжение
47. Работа с массивами. Окончание


^^^   42. MySQL: Коррекция таблиц, дамп/загрузка данных

Что помогает работать с MySQL.

По просьбам web-зрителей, хочу обратить свое внимание в несколько абзацев текста, посвященного работе с MySQL. Честно говоря, не очень понимаю, зачем переписывать на свой лад огромное количество документации по MySQL, но коли просят...

Итак. Чего полезного умеет MySQL.

Для начала надо понять, что MySQL - это движок баз данных, рассчитанный на поддержку различных баз данных и одновременное обслуживание большого количества запросов от разных пользователей. Другими словами, не надо думать, что для каждого сайта надо "поднимать" и настраивать отдельную копию MySQL. Для каждого пользователя создается своя область хранения информации, которая называется database. И если доступ к MySQL вам предоставил провайдер, то наверняка он предоставил вам все права на вашу личную database, в которой вы и будете хозяйничать.

Далее. Обращаться с запросами к MySQL можно как для работы с данными, так и для настройки самого MySQL или для настройки базы. Для работы с MySQL можно использовать любой SQL-клиент, который вам наиболее привычен и удобен. Лично я использую стандартного клиента MySQL, работающего в UNIX-shell. Обычное общение с MySQL сильно напоминает работу в shell или в DOS (для тех, кто никогда не видел в глаза UNIX).

Для начала, вы подключаетесь к MySQL, что у меня на моем хостинге в masterhost.ru выглядит как:

[atos]$ /usr/local/bin/MySQL -h MySQL.int -u 21ru -p 21ru
где есть вызов самого клиента MySQL с указанием адреса сервера MySQL (MySQL.int - это адрес отдельного сервера с MySQL), указанием имени пользователя и названия базы данных (database).

После нажатий enter-а, MySQL запросит у меня пароль, проверит его и пустит в свой shell:

Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  CoMySQLands end with ; or \g.
Your MySQL connection id is 3060933 to server version: 3.23.49

Type 'help;' or '\h' for help. Type '\c' to clear the buffer

MySQL>
Вот так. И далее я начинаю общаться с MySQL.

Это подключение ничем не отличается от тех, которые совершают ваши скрипты. Они тоже подключаются к MySQL, отправляют в него запрос, получают ответ и закрывают сессию. Работа в MySQL shell отличается только тем, что мы долго держим открытой сессию, последовательно передавая MySQL всевозможные запросы. Кстати, много уроков назад, где мы создавали нашу базу данных, я цитировал именно ответы данного MySQL-клиента.

* * *

Итак. Что же можно сделать с MySQL, кроме обычных запросов к таблицам базы данных.

Можно модифицировать сами эти таблицы. Для этого используется запрос с ключевым словом alter table. Например, нам надо добавить в существующую таблицу tbl_text поле text_length в формате int с изначальным значением 100. Пишем:

alter table tbl_text add column text_length int not null default 100; [enter]
и все записи в таблице tbl_text получат новое поле text_length со значением 100.

Кстати, в shell MySQL можно писать запросы в несколько строк. MySQL не начнет интерпретировать ваш запрос, пока вы не закончите его точкой с запятой ";" и enter-ом, следующим за этим знаком.

Подробнее о возможных преобразованиях таблиц можно почитать тут: http://www.MySQL.com/doc/A/L/ALTER_TABLE.html.

И прошу заметить, что правильно построенный запрос на основе alter table никогда не порушит ваших данных, занесенных ранее в исправляемую таблицу.

* * *

Еще хочется обратить внимание на проблемы многих пользователей, "перевозящих" данные из других баз данных.

Зачастую получается так, что нельзя подключиться сразу к двум базам и перелить данные из одной БД в другую. Особенно, когда старая база данных в старом неведомом формате или вообще представляет собой что-то самодельное. В подобных случаях пользователь начинает писать скрипт, который "сосет" данные из файла, парсит их и при помощи банального insert-а укладывает в таблицы. В общем, в этом подходе нет ничего предосудительного, особенно, когда вам необходимо залить десяток-другой тысяч строк. Но если ваша база похожа на настоящую, и количество записей измеряется миллионами, то лучше воспользоваться специальной возможностью MySQL, которая зовется LOAD DATA INFILE.

Это очень гибкая функция MySQL, позволяющая прочесть из файла данные в базу. При этом, гибкость заключается в том, что вы можете описать формат хранения данных в файле, указать диапазон данных, подлежащих заливке и так далее. Подробно эта функцию описана тут: http://www.MySQL.com/doc/L/O/LOAD_DATA.html. И, что особенно важно, скорость заливки данных при помощи LOAD DATA намного превышает построчную скорость заливки insert-ом. Надеюсь, функция LOAD DATA окажется вам полезной.

* * *

Ну а что делать, если у вас есть необходимость перенести не только ваши данные, но и всю конфигурацию базы: таблицы, правила, индексы и так далее? В подобном случае нам очень полезно будет дампироование базы.

Что такое дамп базы. Это вывод структуры и данных из базы в формате SQL-запросов. Поясню. Если у нас в базе есть таблицы, а в таблицах данные, то в дамп мы получим SQL-запросы на построение таблиц и insert-ы для заполнения этих таблиц данными. Да что я вам рассказываю, вы сами можете сделать дамп любой своей базы посмотреть на него.

В пакете MySQL есть mysqldump который этим и занимается. Запуск mysqldump очень похож на запуск стандартного клиента MySQL, о котором я писал вначале этого выпуска. То есть, указывается база, указывается пользователь и название database, дампировать которую мы собрались.

Например:

[atos]$ /usr/local/bin/mysqldump -h MySQL.int -u 21ru -p 21ru
Разумеется, mysqldump имеет массу всевозможных параметров. Например, указав в строке вызова -d можно вывести в дамп только структуру таблиц, без данных. Это особенно удобно, когда вам надо создать копию структуры базы на другом сервере, не перенося при этом данных. Так же можно указать конкретные таблицы, дамп которых вам необходим и так далее.

Полный список ключей mysqldump можно получить:

[atos]$ mysqldump -help
Получив на руки дамп базы, вы можете залить его практически в любую другую БД SQL, а уж на любой другой MySQL-север - это точно. Залить данные из дампа просто:
[atos]$ /usr/local/bin/mysql -h MySQL.int -u 21ru -p 21ru < base.dump
где первая часть строки до знака "<" -- подключение к базе данных, а вторая - файл, содержащий дамп базы.

MySQL подключится к базе (не забыв спросить у вас пароль) и построчно выполнит все инструкции из файла base.dump. Как видите, все просто. Попробуйте сами.

Итак. Если вы хотите перенести свой проект, написанный на php+MySQL, то это можно сделать в несколько несложных шагов:

  1. Делаем дапм базы (с данными, если нужна копия проекта и без данных, если нужен новый чистый проект).
  2. Копируем файлы самого сайта
  3. Правим пути в php-файлах, если они изменились.

И все.

Таким образом я позавчера создал копию форума http://forum.kurepin.ru на новом месте. Я не мог воспользоваться программой инсталляции форума, так как мой вариант уже очень сильно отличается от своего прародителя.

Кроме того, вы можете пользоваться mysqldump для бэкапа вашей базы. Не забывайте, что MySQL отличается от своих старших братьев отсутствием транзакций. Другими словами, поданную MySQL команду уже никак нельзя отменить. И если вы написали delete from table tbl_name и нажали enter прежде чем ограничить удаление какими-то критериями, таблица tbl_name будет безвозвратно очищена от данных. И в этом случае вас очень выручит mysqldump сделанный незадолго до этого.

* * *

Это все, что касается работы MySQL на удаленном хостинге. Если же вы сами для себя установили MySQL и администрируете его, то вам необходимо знать много больше. Вам необходимо уметь создавать базы, пользователей, правильно определять уровни доступа пользователей к базе, защищать MySQL от внешних атак, настраивать буферизацию, эффективное управление памятью сервера, языками сортировки, и так далее.

Но это уже темы для других выпусков, которыми я буду перемежать выпуски по web-программированию.


^^^   43. MySQL: Использование индексов

Продолжая вчерашнюю тему MySQL, хочу поговорить с вами об индексации таблиц.

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

Что же это за зверь такой - индексация? Попробую объяснить на примере. Вспомните любую публичную библиотеку. Пусть детскую или даже школьную. Помните зал со стеллажами книг? И даже если вы были очень давно в библиотеке, вы прекрасно знаете, что все книги в этом уважаемом заведении расставлены не абы как, не в порядке их поступления в библиотеку (как поступают данные в базу), а по каким-то правилам. Обычно, книги разносят по темам, авторам и по алфавиту.

Я думаю, излишне объяснять, зачем все это делается, и почему библиотекари так ревностно следят за порядком размещения книг на стеллажах. Но я позволю себе обратить ваше внимание на сравнительную эффективность поиска в такой структурированной системе, которой, кстати, обычно пренебрегают при построении и использовании компьютерных баз данных.

Предположим, вы в библиотеке ищите книгу "Как выращивать цветы в сухой местности". Если вы начнете тупо перебирать все книги в библиотеке, то у вас на это уйдет не один день или даже не один месяц, если это крупная библиотека. Но если вы знаете автора или год или тему книги, то, подойдя к соответствующим стеллажам, вы найдете издание за несколько минут, а то и секунд. Как, собственно, это и бывает в библиотеке.

Так какого же хрена, прошу прощения за резкость, мы заставляем наши компьютеры искать необходимые нам данные методом тупого перебора? Только потому, что они это делают быстро? Да, быстро. Но это если поиск надо произвести в тысячах записей. А если речь идет о миллионах? Или вы думаете, что ваша база данных не так велика, чтобы заниматься ее оптимизацией? Ошибаетесь, дорогие мои. Как только к вашей базе обратятся сотни человек, так ваши тысячи записей тут же превратятся для движка баз данных в миллионы! И ваш провайдер совершенно резонно сделает вам замечание.

Итак. В том, что базы данных надо индексировать - не сомневается ни один здравомыслящий программист. Правильно построенные индексы позволяют находить нужную информацию "в одно касание".

Как это происходит технически - нам знать не обязательно. Достаточно иметь в виду, что MySQL, как и любой другой движок баз данных, использует дополнительное место на диске для хранения индексных файлов. Это надо знать, только для того, чтобы не плодить ненужных индексов. Во всем надо знать меру. Даже в таком полезном деле, как индексация таблиц. Так давайте сразу и определимся с тем, что нам надо индексировать.

А индексировать надо те поля таблицы, по которым происходит поиск или сортировка данных. Например, у нас есть таблица books и таблица authors. В первой мы храним информацию о книгах, а во второй - информацию об их авторах. Разумеется, самое логичное для подобной базы - искать в ней книги по названию и по автору. Не имея индексации такой базы, примитивный поиск по первым буквам произведения вынудит компьютер просмотреть все записи в таблице, чтобы выдать полный результат. Если база большая, на это уйдет время. Если же мы добавим в таблицу books индекс по полю book_name (название книги), то MySQL создаст индекс этой таблицы. То есть, отсортирует таблицу по указанному полю и расставит метки и ссылки на ячейки в реальной таблице.

Другими словами, индекс - это копия таблицы без данных, но отсортированная по определенным правилам, где каждая ячейка - есть ссылка на родительскую ячейку в основной таблице. Надеюсь, что выразился достаточно ясно. Построив индекс по полю book_name, наш компьютер найдет по первым буквам нужные нам произведения практически мгновенно, ведь ему теперь не надо перебирать всю таблицу, а достаточно посмотреть на ту ее часть, где хранятся записи на нужную букву.

Еще проще говоря, если мы ищем книги на букву "М", то компьютер не станет перебирать записи, начинающиеся на другие буквы, прекрасно зная, что там нет записей, удовлетворяющих запросу. И если в нашей книжной базе находится 100.000 книг, среди которых только 30 на букву Ж, то по запросу "найти все книги на букву Ж", компьютер переберет только 30 записей при наличии индекса, или переберет 100.000 записей при его отсутствии. По-моему, польза очевидна.

Кстати, индексация текстовых полей - занятие чуть более сложное, чем индексация полей другого типа. Поясню. Цифровые, буквенные, булевы, поля дат, времени и другие - индексируются, как правило, без каких либо дополнительных размышлений.

Скажем, если в таблице книг есть поле book_date, хранящее дату публикации книги, то добавление индекса к такому полю будет выглядеть примерно так:

alter table tbl_books add index i_date (book_date);
Эта директива указывает MySQL создать индекс по полю book_date. Теперь компьютеру не составит труда найти все книги 1993-го года или выстроить все найденные книги в порядке даты их публикации. Точно так же можно создать индексы для других полей, по которым нам интересно производить поиск или сортировку.

Создание же индекса по текстовому полю осложняется только тем, что надо указать количество символов от начала записи, по которым надо построить индекс. И тут вы уже сами должны оценить размер базы, похожесть первых символов разных записей и так далее. Например, если мы строим индекс по названиям улиц, то нам не надо индексировать их по всей длине, а можно предположить, что подавляющее большинство записей начнут различаться уже где-то на пятой букве. А если мы индексируем коды шариковых подшипников, то индексировать лучше как можно больше символов, ибо большое количество изделий могут нести коды, отличающиеся только последними буквами или цифрами.

Иногда нам не надо специально строить индексы. Достаточно того, что MySQL сам построит индексы по полю, если при объявлении структуры таблицы мы зададим полю уникальность UNIQUE, говорящую о том, что данное поле не может хранить два одинаковых значения.

Другое ключевое слово, создающее индексы - KEY. Помните объявление primary key, которое я обязательно использую в каждой таблице для id-поля. Если вы хотите увидеть, какие поля в таблице проиндексированы, дайте команду MySQL:

desc tbl_name; 
и MySQL выведет всю информацию о таблице tbl_name, включая отметки об индексации полей.

Или более подробно, только об индексах:

show index from tbl_name;
Остается еще отметить, что в индексе может участвовать множество полей. Не обязательно одно. Если в базе накладных чаще всего производится поиск по сумме с учетом диапазона дат, то логично создать индекс по этим двум полям: сумма и дата. Именно, создать один индекс по двум полям, а не два индекса по каждому полю!
alter table tbl_name add index i_name (field1, field2);
Поиск по уникальным индексам производится чуть быстрее, поэтому, если значение какого-то поля должно быть обязательно уникальным - не поленитесь отметить это в конструкции таблицы. MySQL отблагодарит вас скоростью своих ответов на ваши вопросы.

О синтаксисе создания индексов лучше всего написано тут: http://www.mysql.com/doc/A/L/ALTER_TABLE.html.

Прежде чем попрощаться, я бы хотел подсказать вам одну полезную директиву MySQL. Называется она explain. Если вы поставите это слово перед любым запросом к базе данных, MySQL не станет выводить вам результат запроса, а покажет подробную информацию о том, какими средствами пришлось воспользоваться и сколько операций пришлось произвести, чтобы получить ответ на ваш запрос. Это волшебное слово explain позволит оценить эффективность любого запроса и отрегулировать все индексы вашей базы.

Удачной индексации, и да прибудет с вами порядок!


^^^   44. Сборка Apache+PHP+GD+MySQL

Хочу предупредить сразу, что на данный выпуск иду не сознательно, а под давлением форума, в котором постоянно муссируются проблемы, связанные со сборкой и установкой программного обеспечения для поддержки web-сайтов. А если говорить точнее, проблемы сборки mysql, apache, php и т.п. в разных вариантах.

Вот и пообещал я, что изложу свои пристрастия и обычаи в этом деле. Ничего нового или оригинального я не скажу. Просто покажу, как это делаю я. Не стоит принимать это за безукоризненное руководство к действию. Скорее, это инструкция для тех, кто сам не может справиться со сборкой данного софта.

Начнем с платформы. Под win'32 ничего, в общем-то собирать не надо, все поставляется уже в скомпилированном виде. Можно устанавливать все компоненты по отдельности, можно скопом. Вариантов много, но все они описаны поставщиками ПО и в дополнительных комментариях не нуждаются. Во всяком случае, мне тут сказать нечего, ибо я категорически отказался использовать windows-платформу для создания web-узлов. Отказался не потому, что имею зуб на Большого Билла, как это сейчас модно, а потому, что устал поднимать "упавшие" сервера и чувствовать ущербность при удаленном доступе к системе и настройке ПО. Я поделюсь с вами пошаговой установкой ПО под любимую мою ОС -- FreeBSD. Версия ОС значения не имеет.

Боже, сколько всякой фигни я уже написал, а к делу так и не приступил. Исправляюсь!

Собираем следующую связку:

Apache (оригинальный)
PHP (4-х версий)
GD 1.3 (версия с поддержкой GIF-а)
MySql (любой версии)

Весь указанный софт является свободно распространяемым, его легко скачать из Сети.

Для начала установим MySql. Копируем дистрибутив MySqL в директорию inst, распаковываем:

# gzip -d mysql-3.23.33.tar.gz
# tar xf mysql-3.23.33.tar

Переходим в создавшуюся директорию mysql-3.23.33 и настраиваем:

# ./configure --help

:читаем ключики настройки. В общем, тут советовать нечего, но я бы рекомендовал обратить внимание на ключи --prefix= и --with-charset=, первый из которых указывает путь к директории, в которую следует устанавливаться, а второй позволяет подключить разные языки, которые необходимы для правильной сортировки и выборки текстовых полей таблиц.

Сделали configure, сделали make, make install и можно запускать mysql. Не стану подробно останавливаться на конфигурации и запуске MySql, так как планирую посвятить этому отдельный выпуск.

Теперь подготовим apache к сборке.

Копируем дистрибутив в директорию inst. Распаковываем аналогичным с MySql способом, переходим в создавшуюся директорию с дистрибутивом и делаем предварительную конфигурацию, например так:

# ./configure --prefix=/home/atos/www

Временно оставляем Апача и беремся за PHP.

Для начала находим в Сети подходящую версию GD, если планируем работать с графикой. Копируем в директорию inst, распаковываем, переходим в создавшийся каталог и даем короткую команду:

# make

GD готова.

Далее распаковываем PHP аналогичным способом и конфигурируем сборку:

# ./configure --help

Прочтите все ключики, найдите все, которые могут оказаться для вас полезными.

После некоторого пыхтения у нас должна сложиться строка конфигурации, вроде этой:

# ./configure --prefix=/home/atos/www/php4 --with-config-file-path=/home/atos/www/php4 --with-mysql=/usr/local/mysql --with-gd=/home/atos/install/PHP/GD/gd1.3 --with-apache=/home/atos/install/apache/apache_1.3.22

Перевожу:

--prefix -- куда устанавливаемся
--with-config-file-path -- где PHP будет искать php.ini
--with-mysql -- где у нас лежит собранный mysql
--with-gd -- где лежит собранный GD
--with-apache -- где лежит дистрибутив apache

Делаем:

#make
#make install

Если мы не ошиблись в путях, то получили модуль PHP, готовый к подключению при сборке apache.

Возвращаемся к дистрибутиву apache и начинаем его собирать:

# ./configure --prefix=/home/atos/www --activate-module=src/modules/php4

Это мы указали место установки Апача и место расположения модуля PHP. Если сборка PHP прошла успешно, модуль окажется в этой директории. # make # make install

Бывает, что при "make" появятся ошибки подключения библиотек и сборка на этом прервется. Это бывает. В этом случае можно попробовать полностью указать путь к модулю PHP:

# ./configure --prefix=/home/atos/www --activate-module=src/modules/php4/libphp4.a

Чаще всего это помогает.

Все, аднака! Можно запускать apache и пользоваться PHP с полной поддержкой MySQL и GD.

Как видите, все достаточно просто. Надо только быть внимательным и не сильно нервничать, когда компилятор будет останавливаться, заявляя, что не нашел какого-нибудь файла.

В заключение выпуска хочу поделиться "ОГРОМНЫМ" секретом сборки PHP для работы в командной строке.

Многие даже не подозревают, что PHP отлично работает в командной строке, выполняя работу, которую традиционно поручают sh или perl. Не знаю как другим, а мне удобно пользоваться одним языком при создании одного проекта. Поэтому я использую PHP для написания скриптов, запускаемых в cron или руками в командной строке ОС.

Так вот. Собирается PHP для командной строки точно так же, как и для apache, только не надо указывать в ./configure --with-apache. И все!

PHP соберется в указанную директорию, где в папочке bin/ будет лежать заветный файл php.

Можете пользоваться!


^^^   45. Работа с массивами. Начало

Одна из тем на форуме forum.kurepin.ru заставила меня обратить еще раз особое внимание на массивы. Попробую сделать небольшой обзор по общению с массивами.

Итак. Массивы бывают обыкновенные и ассоциативные. Отличаются они только тем, что в обыкновенных массивах ключами служат целые числа, а в ассоциативных -- сочетания различных знаков.

$personal[1];      // обращение к ячейке обыкновенного массива;
$personal['vova']; // обращение к ячейке ассоциативного массива;
Кстати, при обращении к ячейке ассоциативного массива, не обязательно ключ ячейки брать в кавычки или скобки. Но я вам настоятельно рекомендую это делать.

Массивы в PHP могут иметь любую вложенность измерений, но традиционно используются массивы одно- и двухмерные.

Массив может хранить любую информацию -- от простых чисел до сложных структур.

Объявить массив просто:

var $array1 = array();                       // пустой массив
var $array2 = array("vova", "nina", "olga"); // массив с данными
Так объявляют обыкновенный массив. Если же нам надо объявить ассоциативный массив с данными или массив, в котором данные "разбросаны" в далекие друг от друга ячейки, нужно указывать кроме значений ячеек, еще и их ключ (название). Ключ от данных отделяется сочетанием "=>".
var $array3 = array(0 => "vova", 11 => "nina",  543 => "olga");
var $array4 = array("sorokin" => "vova", "bulgakova" => "nina", "zhezlova" => "olga");
В первом случае мы видим массив имен, присвоенных ячейкам с конкретными номерами (все промежуточные ячейки заявлены пустыми), а во втором -- ячейки с именами имеют вместо нумерации -- фамилии.

Чтобы узнать имя Сорокина, нам теперь достаточно спросить массив:

echo $array4['sorokin'];
В общем, это все, что важно знать об объявлении массивов.

Теперь поговорим о том, что мы можем с массивами делать. Разумеется, я не буду останавливаться на том, что в массивах можно хранить данные: читать их, удалять и заменять на новые.


Как проверить существование массива?

Надо заметить, что массив, в стандартных ситуациях, ведет себя так же, как обыкновенная переменная.

if(!isset($array_name)) echo "Нет такого массива!";


Как уничтожить массив?

unset($array_name);
или одну из его ячеек:
unset($array_name[n]);


Как проще всего распечатать содержимое массива?

echo implode(',',$array_name);
где implode -- функция объединения с разделителем.

В данном случае, массив выступает в роли списка переменных.


Если нужно создать массив для одной операции?

Тогда можно воспользоваться функций list(), объединяющей перечисленные в ней переменные в единый массив.

$str="один,два,три";
$list($one, $two,$three)=explode(',',$srt);
Теперь каждая переменная содержит свое слово.

И распечатать:

echo implode(',',list($one, $two, $three));
Можно, конечно, и просто перебрать содержимое массива в цикле:
while($i<count($array_name)) echo $array_name[$i++];


Как изменить размеры массива?

Вам не надо заботиться о выделении памяти для новых ячеек массива. PHP сам выделит их столько, сколько потребуется. Но если вы задумали обрезать массив или вырезать из массива определенный кусок, можете воспользоваться функцией array_splice().


Как выполнить операцию над каждым элементом массива?

Традиционно, для этого используется цикл, подобный тому, что мы только что использовали для распечатки массива. Но если операции проводятся разные, их много или вам просто хочется изящнее написать код, можете воспользоваться функцией array_walk(), которая обойдет ваш массив, передавая каждый элемент массива в указанную функцию, созданную вами.

Например,

function print_arr($str)
 {
  echo $str,"
  \n";
  $str.=" - done";
 } 

$arr=array("one","two","three");
array_walk($arr,'print_arr');
распечатает на экране все элементы массива $arr;

Если же вам необходимо произвести преобразование содержимого ячеек и получившиеся данные записать обратно в ячейки, вам придется указать это явно -- дать ссылку на реальный экземпляр ячейки в памяти. Как и в языке C, в PHP это делается добавлением перед переменной амперсанта "&". Скажем, если бы на надо было не просто распечатать содержимое ячеек, как в приведенном примере, а добавить слово "done" после каждого слова и снова распечатать содержимое массива, то пример выглядел бы так:

function print_arr(&$str)
 {
  echo $str,"\n";
  $str.=" - done";
 } 

$arr=array("one","two","three");
array_walk($arr,'print_arr');
echo "\n";
array_walk($arr,'print_arr');
а результат так:
one
two
three

one - done
two - done
three - done
Вот такая полезная функция, которой редко пользуются.


Как удалить из массива дубликаты?

Очень просто. Для этого существует функция array_unique, которая вернет вам массив, сократив его до массива, не содержащего повторения.


Как сравнить два массива?

И тут PHP нас заставляет поскучать, ибо функция array_dif все сделает за нас. Передайте этой функции два массива и она возвратит вам все элементы первого массива, не найденные во втором. Кстати, вторым массивом можно не ограничиваться, функция принимает для сравнения любое количество массивов через запятую.

$result = array_diff ($array1, $array2,$array3,:);


Как объединить массивы?

В PHP массивы объединяет функция array_merge, правда, никто вам не запретит пройтись циклом по одному массиву, прочесть его по ячейкам и дописать данные в конец другого массива. Честно говоря, одномерные массивы в PHP можно "складывать" обычным плюсом:

$array3=$array1+$array2;
Но! Надо помнить, что сложение массивов игнорирует дубликаты. Если в первом массиве уже есть подобная запись, то она не копируется из второго. При необходимости объединения всех элементов массива, включая повторяющиеся, можно воспользоваться функцией array_merge_recursive.

Данный выпуск был подготовлен при подглядывании в книгу Хьюгса Стерлинга "PHP 4.0. Руководство разработчика".


^^^   46. Работа с массивами. Продолжение

Продолжаем заниматься массивами в режиме вопрос-ответ.

Как выстроить ячейки массива в обратном порядке (обернуть масссив)?

Если массив не очень большой, логично воспользоваться функцией array_reverse.

$arr=array_reverse($arr);
Но эта функция не слишком быстра на больших массивах. Для сортировки большого массива больше подходит вариант с циклом for:
for($i=count($arr)-1; $i>=0; $i--)
{
 $arr2[$j++]=$arr[$i];
}
$arr=$arr2; unset($arr2);
т.е. обходим массив $arr от конца к началу и записываем данные в новый массив, начиная с нулевой ячейки.

Этот вариант работает быстрее, но кушает память во время выполнения, об этом тоже не стоит забывать.


Как получить сразу несколько элементов массива?

Предположим, что вам надо получить три элемента массива в разные переменные. Можно воспользоваться традиционным присваиванием:

$first_name=$person[1];
$last_name=$person[2];
$fax=$person[14];
а можно воспользоваться функцией array_slice совместно с хорошо известной нам функцией list.
list($first_name,$last_name,$fax)=array_slice($person,1,2,14);
Может быть вам покажется, что функция array_slice и не самая полезная, но, поверьте, бывают случаи, когда она очень удобна. Например, когда надо из большого массива выделить выборочные данные в маленький массив.
$basket=array_slice($shop,4,6,12,18,22);


Как найти элементы, удовлетворяющие определенному условию?

Тут, друзья мои, лучше всего подходят традиционные методы работы с массивами -- циклы. Для обхода массива по ячейкам можно использовать любые циклы, но традиционно, используется цикл for для обыкновенных массивов и foreach для ассоциативных. С первым циклом вы уже знакомы, а циклом foreach можно воспользоваться, например, так:

foreach($arr as $k => $v)
{
 echo "arr['$k']=$v\n";
}
Этот цикл нарисует вам все содержимое ассоциативного массива в столбик. Кстати, этот метод вполне приемлем и для числовой индексации массивов, но он работает медленнее, чем цикл for, поэтому, использовать его следует по прямому назначению.


Как отсортировать массив?

Для сортировки массива в PHP существуют различные функции.

sort() -- сортирует массив самым, что ни на есть, традиционным способом. В качестве дополнительно параметра сортировки можно указать: сортировку обыкновенную, сортировку числовую и сортировку строковую.

rsort() -- используется для сортировки ассоциативных массивов.

arsort() и asort() -- сортируют массив в обратно порядке, с той лишь разницей, что arsort сортирует массивы ассоциативные, а asort сортирует массивы с числовой индексацией.

ksort() и krsort() -- сортируют ассоциативные массивы по ключам, что бывает очень важно. Первая функция сортирует в прямом, а вторая -- в обратном порядке.

natsort() -- не менее полезная сортировка, осуществляющая упорядочивание элементов массива в "натуральном", жизненно-очевидном порядке. Покажу на примере из учебника PHP -- сразу станет все понятно:

Standard sorting
Array
(
    [0] => img1.png
    [1] => img10.png
    [2] => img12.png
    [3] => img2.png
)

Natural order sorting 
Array
(
    [3] => img1.png
    [2] => img2.png
    [1] => img10.png
    [0] => img12.png
)

natcasesort() -- делает то же самое, но не обращая внимания на регистр букв.

usort() и uksort() -- функции сортировки по правилам, определяемым пользователем. Я не буду подробно останавливаться на этом виде сортировки. Так как используют ее крайне редко. Если доведется, посмотрим эту функцию на "живом" примере.

Данный выпуск был подготовлен при подглядывании в книгу Хьюгса Стерлинга "PHP 4.0. Руководство разработчика".


^^^   47. Работа с массивами. Окончание

Разобравшись со стандартными массивами, перейдем к ознакомлению с особенностями массивов ассоциативных. Как вы уже знаете, ассоциативный массив отличается от массива с числовой индексацией только тем, что в качестве индекса используются цепочки символов и букв. В любом их сочетании. И первый вывод, который сам напрашивается -- любая операция, проводимая над ассоциативным массивом, может быть с таким же успехом проведена над массивом с числовой индексацией. Но не всегда работает обратно правило, поэтому пройдемся по функциям, полезным для работы с ассоциативными массивами.

Если в обыкновенных массивах мы чаще всего работаем с данными, используя ключи индексации только в качестве идентификатора ячейки, то в ассоциативных массивах имена ячеек и их содержимое достаточно часто используются наравне, как хранилище данных. Для простоты даже можно представить себе, что ассоциативный массив это обыкновенный двумерный массив, где в одном измерении содержится ключ ассоциативного массива, а в другом -- данные.

Надеюсь, у вас уже нет проблем с пониманием, что такое ассоциативный массив? Тогда перейдем к делу.


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

Очень просто. Так же, как и любую переменную.

if(isset($arr['test']) echo "Ячейка с именем 'test' -- существует";

Но эта функция только сообщит нам: выделено ли место в памяти под ячейку с таким именем или нет. И ответ будет положительным даже если ячейка будет содержать 0 или NULL.

Если же вы хостите знать, не пуста ли ячейка, воспользуйтесь функцией empty().


Как удалить ячейку в ассоциативном массиве?

Если вы желаете обнулить ячейку, можете присвоить ей ноль или пустую строку:

$arr['test']='';
$arr['test']=0;

Если же вы задумали удалить ячейку вообще, т.е. удалить ее из памяти, можно воспользоваться функцией деструкции переменной unset():

unset(arr['test']);


Как выяснить, содержится ли в массиве какое-то конкретное значение?

Для того, чтобы получить ответ на этот вопрос, разработчики PHP включили стандартную функцию in_array:

if(in_array('искомое значение',$arr)) :;

Функция in_array попросту производит поиск в массиве и возвращает TRUE, если значение найдено или FALSE в противном случае.

Однако, есть интересная и важная тонкость в таком поиске. Система автоматического приведения типов в PHP может сыграть с вами злую шутку. Что, если вы ищите число 12345 в массиве, где есть строка "012345"? PHP решит, что это значение вам вполне подходит, так как можно спокойно отбросить незначащий ноль в числе 012345.

Чтобы подобного избежать, в четвертой версии PHP в функцию in_array добавлен третий параметр, определяющий: стоит ли производить поиск с учетом разных типов или нет.

Можете сами произвести интересные эксперименты, подставляя в массив разные типы и выискивая их. Список типов в PHP можно лицезреть здесь.

Как уже было отмечено, хоть идеологическая разница между ключом и значением очевидна, технически они ничем друг от друга не отличаются. Например, у вас есть массив, где названия должностей -- ключи ячеек, а фамилии сотрудников -- их содержимое. По должности вы всегда можете найти фамилию ответственного:

$man=$arr['director']; // выясняется, что это Иванов

Но с такой же легкостью вам может понадобиться узнать название должности по фамилии сотрудника. И эту задачу можно свести к предыдущей, если...


Как можно обернуть ассоциативный массив?

Чтобы поменять ключи и значения местами, можно воспользоваться функцией PHP array_flip. Думаю, тут и пояснять нечего.


Как сортировать ассоциативные массивы?

Как и для обыкновенных массивов, для сортировки ассоциативных существуют всевозможные сортировки: asort(), arsort(), ksort(), krsort(), uasort() и uksort(), сортирующие соответственно: прямо, в обратном порядке, прямо по ключам, обратно по ключам, прямо по пользовательской функции и обратно по пользовательской функции.


Как соединить два ассоциативных массива?

Можно:

1. Воспользоваться функцией array_merge.

2. Сложить массивы обычным плюсом. Вы уже знаете как.

Однако, надо соблюдать осторожность. Если при слиянии, в массивах окажутся ячейки с одинаковым индексом, ячейка будет перезаписана данными того массива, который указан последним в списке аргументов к array_merge. В принципе, этого можно избежать, если воспользоваться функцией array_merge_recursive(), которая объединяет значения одинаковых ячеек. Производит их "слияние".

Все, пожалуй, теперь я могу быть уверенным в том, что проблем с массивами в PHP у вас не возникнет. А если проблемы все же возникнут, вы знаете, куда обращаться...

P.S. Данный выпуск был подготовлен при подглядывании в книгу Хьюгса Стерлинга "PHP 4.0. Руководство разработчика".


Продолжение следует :)