- •Пособие по работе в среде программирования php 4
- •1.Установка php
- •Настройка Apache для работы с php
- •Тестирование php
- •Установка дополнительных модулей
- •2. Характеристика языка php
- •Интерпретатор или компилятор?
- •Достоинства и недостатки интерпретатора
- •Использование php в Web
- •3. Переменные, константы, выражения
- •Переменные
- •Integer
- •Действия с переменными
- •1.Присвоение значения.
- •2.Проверка существования.
- •3.Уничтожение.
- •4.Определение типа переменной.
- •Константы
- •Выражения
- •Операции
- •4. Работа с данными формы
- •5. Конструкции языка
- •6. Ассоциативные массивы
- •Операции над массивами
- •1.Одинаковые ключи
- •2.Нулевой ключ
- •7. Функции и области видимости
- •8. Строковые функции
- •9. Работа с массивами
- •10. Математические функции
- •11. Работа с файлами
- •12. Работа с каталогами
- •13. Каналы и символические ссылки
1.Одинаковые ключи
Первый недостаток довольно фундаментален: мы не можем одновременно перебирать массив в двух вложенных циклах или функциях. Причина вполне очевидна: второй вложенный for "испортит" положение текущего элемента у первого for’а. К сожалению, эту проблему никак нельзя обойти (разве что сделать копию массива, и во внутреннем цикле работать с ней, но это не очень-то красиво). Однако практика показывает, что такие переборы встречаются крайне редко.
2.Нулевой ключ
А что, если в массиве встретится ключ 0 (хотя для массивов имен это, согласитесь, маловероятно)? Давайте еще раз посмотрим на первый цикл перебора:
for(Reset($Names); ($k=key($Names)); Next($Names))
echo "Возраст $k — {$Names[$k]} лет\n";
В этом случае выражение ($k=key($Names)), естественно, будет равно нулю, и цикл оборвется, чего бы нам совсем не хотелось.
Именно по этим причинам разработчики PHP придумали другой, хотя и менее универсальный, но гораздо более удобный метод перебора массивов, о котором сейчас и пойдет речь.
Прямой перебор массива
В отличие от косвенного перебора (когда сначала вычисляется очередной ключ, а уж затем по нему косвенно находится значение элемента массива), прямой перебор лаконичнее и гораздо более прост. Идея метода заключается в том, чтобы сразу на каждом "витке" цикла одновременно получать и ключ, и значение текущего элемента.
Классический перебор
Давайте опять вернемся к нашему примеру, в котором массив $Names хранил связь имен людей и их возрастов. Вот как можно перебрать этот массив при помощи прямого перебора:
for(Reset($Names); list($k,$v)=each($Names); /*пусто*/)
echo "Возраст $k – $v\n";
В самом начале заголовка цикла мы видим нашу старую знакомую Reset(). Дальше переменным $k и $v присваивается результат работы функции each(). Третье условие цикла попросту отсутствует (чтобы это подчеркнуть, на его месте включен комментарий).
Что делает функция each()? Во-первых, возвращает небольшой массив, нулевой элемент которого хранит величину ключа текущего элемента массива $Names, а первый – значение текущего элемента. Во-вторых, она продвигает указатель текущего элемента к следующей позиции. Следует заметить, что если следующего элемента в массиве нет, то функция возвращает не список, а false.
Именно поэтому она и размещена в условии цикла for. Становится ясно, почему мы не указали третий блок операторов в цикле for: он просто не нужен, ведь указатель на текущий элемент и так смещается функцией each().
Перебор в стиле PHP 4
Прямой перебор массивов применялся столь часто, что разработчики PHP решили в четвертой версии языка добавить специальную инструкцию перебора массива – foreach. Мы уже рассматривали ее ранее. Вот как с ее помощью можно перебрать и распечатать наш массив людей:
foreach($Names as $k=>$v) echo "Возраст $k — $v\n";
Просто, не правда ли? Рекомендую везде, где не требуется совместимостьс PHP третьей версии, использовать именно этот способ перебора, поскольку он работает с максимально возможной скоростью – даже быстрее, чем перебор списка при помощи for и числового счетчика.
Есть и еще одна причина предпочесть этот вид перебора "связке" цикла for с eaсh(). Дело в том, что при применении foreach мы указываем имя перебираемого массива $Names только в одном месте, так что когда вдруг потребуется это имя изменить, нам достаточно будет поменять его только один раз.
Наоборот, использование Reset() и each() заставит нас в таком случае изменять название переменной в двух местах, что потенциально может привести к ошибке. Представьте, что произойдет, если мы случайно изменим операнд each(), но сохраним параметр Reset()!
Списки и строки
Есть несколько функций, которые чрезвычайно часто используются при программировании сценариев. Среди них – функции для разбиения какой-либо строки на более мелкие части (например, эти части разделяются в строке каким-то специфическим символом типа | ), и, наоборот, слияния нескольких небольших строк в одну большую, причем не впритык, а вставляя между ними разделитель. Первую из этих возможностей реализует стандартная функция explode(), а вторую – implode(). Рекомендую обратить особое внимание на указанные функции, т. к. они применяются очень часто.
Функция explode() имеет следующий синтаксис:
list explode(string $token, string $Str [, int $limit])
Она получает строку, заданную в ее втором аргументе, и пытается найти в ней подстроки, равные первому аргументу. Затем по месту вхождения этих подстрок строка "разрезается" на части, помещаемые в массив-список, который и возвращается. Если задан параметр $limit, то учитываются только первые ($limit-1) участков "разреза". Таким образом, возвращается список из не более чем $limit элементов. Это позволяет нам проигнорировать возможное наличие разделителя в тексте последнего поля, если мы знаем, что всего полей, скажем, 6 штук. Вот пример:
$st="4597219361|Иванов|Иван|40|ivan@ivanov.com|Текст, содержащий (|)!";
$A=explode("|",$st,6); // Мы знаем, что там только 6 полей!
// теперь $A[0]="Иванов", ... $A[5]= "Текст, содержащий (|)!"
list($Surname,$Name,$Age,$Email,$Tel)=$A; // распределили по переменным
Конечно, строкой разбиения может быть не только один символ, но и небольшая строка. Не перепутайте только порядок следования аргументов при вызове функции! Функция implode() и ее синоним join() производят действие, в точности обратное вызову explode().
string implode(string $glue, list $List) или
string join(string $glue, list $List)
Они берут ассоциативный массив (обычно это список) $List, заданный в ее первом параметре, и "склеивают" его значения при помощи "строки-клея" $glue во втором параметре. Примечательно, что вместо списка во втором аргументе можно передавать любой ассоциативный массив – в этом случае будут рассматриваться только его значения.
Рекомендую вам чаще применять функции implode() и explode(), а не писать самостоятельно их аналоги. Работают они очень быстро.
Сериализация
Возможно, после прочтения описания функций implode() и explode() вы обрадовались, насколько просто можно сохранить массив, например, в файле, а затем его оттуда считать и быстро восстановить. Если вас посетила такая мысль, то, скорее всего, вы уже успели в ней разочароваться: во-первых, таким образом можно сохранять только массивы списки (потому что ключи в любом случае теряются), а во-вторых, ничего не выйдет с многомерными массивами.
Давайте теперь предположим, что нам все-таки нужно сохранить какой-то массив (причем неизвестно заранее, сколько у него измерений) в файле, чтобы потом, при следующем запуске сценария, его аккуратно загрузить и продолжить работу. Можно, конечно, начинать писать универсальную рекурсивную функцию для упаковки массива в строку (ведь в файлы можно писать только строки), и еще одну, которая будет эту строку разбирать и восстанавливать на ее основе массив в исходном виде.
Рекомендую проделать это в качестве упражнения, заодно постарайтесь добиться, чтобы упакованные данные занимали минимум объема. Это пригодится вам в будущем, при работе с Cookies.
Однако вскоре вы поймете, что все не так просто в PHP, в котором работа со ссылочными переменными очень и очень ограничена. Особенно будет тяжело с функцией распаковки строки.
И тут нам на помощь опять приходят разработчики PHP. Оказывается, обе функции давным-давно реализованы, причем весьма эффективно со стороны быстродействия (но, к сожалению, непроизводительно с точки зрения объема упакованных данных).
Называются они, соответственно, Serialize() и Unserialize().
Функция Serialize() возвращает строку, являющуюся упакованным эквивалентом некоего объекта $Obj, переданного во втором параметре.
string Serialize(mixed $Obj)
При этом совершенно не важно, что это за объект: массив, целое число…. Да что угодно. Например:
$A=array("a"=>"aa", "b"=>"bb", "c"=>array("x"=>"xx"));
$st=Serialize($A);
echo $st;
// выведется что-то типа нечто:
//
a:2:{s:1:"a";s:2:"aa";s:1:"b";s:2:"bb";s:1:"c";a:1:{s:1:"x";s:2:"xx";}}
Функция Unserialize(), наоборот, принимает в лице своего параметра $st строку, ранее созданную при помощи Serialize(), и возвращает целиком объект, который был упакован.
mixed Unserialize(string $st)
Например:
$a=array(1,2,3);
$s=Serialize($a);
$a="bogus";
echo count($a); // выводит 1
$a=Unserialize($s);
echo count($a); // выводит 3
Еще раз отмечу: сериализовать можно не только массивы, но и вообще что угодно. Однако в большинстве случаев все-таки используются массивы. Механизм сериализации часто применяется также и для того, чтобы сохранить какой-то объект в базе данных, и тогда без сериализации практически не обойтись.