Самоучитель по PHP 4
.pdfГлава 8. Работа с данными формы |
151 |
Трансляция массивов
В сущности, мы уже рассмотрели почти все возможности PHP по автоматической трансляции данных формы. Напоследок взглянем на еще одно полезное свойство PHP. Пусть у нас есть такая форма (листинг 8.7):
Листинг 8.7. Трансляция массивов
Имя: <input type=text name=Data[name]><br> Адрес: <input type=text name=Data[address]><br>
Город:<br>
<input type=radio name=Data[city] value=Moscow>Москва<br>
<input type=radio name=Data[city] value=Peter>Санкт-Петербург<br> <input type=radio name=Data[city] value=Kiev>Киев<br>
Можно догадаться, что после передачи подобных данных сценарию на PHP в нем будет инициализирован ассоциативный массив $Data с ключами name, address и city (асcоциативные массивы мы также затрагивали пока только вскользь, но очень скоро этот пробел будет достойно восполнен). То есть, имена полям формы можно давать не только простые, но и представленные в виде одномерных ассоциативных массивов.
Забегая вперед, скажу, что в сценарии к отдельным элементам формы можно будет обратиться при помощи указания ключа массива: например, $Data['city'] обозначает значение той радиокнопки, которая была выбрана пользователем, а $Data["name"] — ее имя. Заметьте, что в сценарии мы обязательно должны заключать ключи в кавычки или апострофы — в противном случае интерпретатором будет выведено предупреждение. В то же время, в параметрах name полей формы мы, наоборот, должны их избегать — уж так устроен PHP.
Если верить официальной документации, то многомерные массивы (то есть, массивы массивов) указывать нельзя. Например, при передаче данных поля,
определенного как <input type=text name=Silly[one][two][three]> в
программе, действительно, создастся массив $Silly, но он будет одномер- ный и с ключом one][two][three — совсем не то, что мы ожидали, не прав- да ли? В принципе, при большом желании можно написать функцию, которая конвертирует такие "испорченные" массивы в нормальное многомерное пред- ставление, но это выходит за рамки нашего обзора.
К счастью, похоже, разработчики PHP поняли, что неработоспособность многомерных массивов при передаче их из формы серьезно снижает популярность PHP. Поэтому они наконец-то включили в PHP поддержку последних. Ура! Например, в моей версии PHP 4.0.3 (самой свежей на момент написания этих строк) они уже работают.
152 |
Часть III. Основы языка PHP |
Впрочем, в документации по-прежнему заявлено, что "многомерные массивы использовать нельзя". Что это — ошибка или злая шутка?..
Как же проверить, можно ли использовать многомерные массивы при обработке форм в вашей версии PHP? Нет ничего проще! Достаточно запустить следующий сценарий (листинг 8.8).
Листинг 8.8. testarr.php: работают ли многомерные массивы?
<?
//оператор @ нужен, для того чтобы подавить предупреждение, если
//переменная еще не была инициализирована.
if(@$go) { if(@$A[10][20]=="Yes") {
echo "<h1>Многомерные массивы работают!";
}else {
echo "Многомерные массивы НЕ работают!";
}
}else {
echo "<h1>Testing, wait...</h1>"; echo "<meta http-equiv=Refresh ";
echo "content=’0; URL=$REQUEST_URI?go=1&A[1][2]=Yes’>";
}
?>
Вот вкратце, как он работает. При первом запуске переменная $go не инициализирована, поэтому управление получает блок, выводящий тэг <meta>. Он заставляет браузер перезагрузить страницу, но уже с параметрами в командной строке go=1&A[1][2]=Yes. Сценарий запускается снова, но уже на этот раз переменная $go равна 1 (потому что именно такое значение было передано в командной строке). Если многомерные массивы поддержаны, то, очевидно, что элемент массива $A[1][2], которому мы присвоили значение Yes в командной строке, будет существовать и равняться Yes. В этом случае мы получим сообщение, что массивами пользоваться можно, а иначе — что они не работают.
Глава 9
Конструкции
языка
Ну вот мы и подобрались к языковым конструкциям. Некоторые из них нами уже применялись, и не раз — например, инструкция if. В этой главе приводится полное описание всех языковых конструкций PHP. Их не так много, и это достоинство PHP. Как показывает практика, чем более лаконичен синтаксис языка, тем проще его использовать в повседневной практике. PHP — отличный пример этому.
О терминологии
Иногда я применяю слово "конструкция", а иногда — "инструкция". В данной книге эти два термина совершенно эквивалентны. Наоборот, термины "опе- ратор" и "операция" несут разную смысловую нагрузку: любая операция есть оператор, но не наоборот. Например, echo — оператор, но не операция, а ++
— операция.
Инструкция if-else
Начнем с самой простой инструкции — условного оператора. Его формат таков:
if(логическое_выражение) инструкция_1;
else
инструкция_2;
Действие его следующее: если логическое_выражение истинно, то выполняется инструкция_1, а иначе — инструкция_2. Как и в любом другом языке, конструкция else может опускаться, в этом случае при получении должного значения просто ничего не делается.
Пример:
if($a>=1&&$b<=10) echo "Все OK";
else echo "Неверное значение в переменной!";
Если инструкция_1 или инструкция_2 должны состоять из нескольких команд, то они, как всегда, заключаются в фигурные скобки. Например:
154 |
Часть III. Основы языка PHP |
if($a>$b) { print "a больше b"; c=$b; } elseif($a==$b) { print "a равно b"; $c=$a; } else { print "a меньше b"; $c=$a; }
Это не опечатка: elseif слитно, вместо else if. Так тоже можно писать, хотя это, по-моему, и не удобочитаемо.
Конструкция if-else имеет еще один альтернативный синтаксис:
if(логическое_выражение): команды;
elseif(другое_логическое_выражение): другие_команды;
else:
иначе_команды; endif
Обратите внимание на расположение двоеточия (:)! Если его пропустить, будет сгенерировано сообщение об ошибке. И еще: как обычно, блоки elseif и else можно опускать.
Использование альтернативного синтаксиса
В предыдущих главах нами уже неоднократно рассматривался пример вставки HTML-кода в тело сценария. Для этого достаточно было просто закрыть скобку ?>, написать этот код, а затем снова открыть ее при помощи <?, и продолжать программу.
Возможно, вы обратили внимание на то, как это некрасиво выглядит. Тем не менее, если приложить немного усилий для оформления, все окажется не так уж и плохо. Особенно, если использовать альтернативный синтаксис if-else и других конструкций языка.
Чаще всего, однако, нужно бывает делать не вставки HTML внутрь программы, а вставки кода внутрь HTML. Это гораздо проще для дизайнера, который, возможно, в будущем захочет переоформить ваш сценарий, но не сможет разобраться, что ему изменять, а что не трогать. Поэтому целесообразно бывает отделять HTML-код от программы, например, поместить его в отдельный файл, который затем подключается к программе при помощи инструкции include (см. ниже). Сейчас мы не будем подробно останавливаться на этом вопросе, но потом обязательно к нему вернемся.
Вот, например, как будет выглядеть наш старый знакомый сценарий, который приветствует пользователя по имени, с использованием альтернативного синтаксиса ifelse (листинг 9.1):
Глава 9. Конструкции языка |
155 |
Листинг 9.1. Альтернативный синтаксис if-else
<?if(@$go):?> Привет, <?=$name?>!
<?else:?>
<form action=<?=$REQUEST_URI?> method=post> Ваше имя: <input type=text name=name><br> <input type=submit name=go value="Отослать!">
<?endif?>
Согласитесь, что даже человек, совершенно не знакомый с PHP, но зато хорошо разбирающийся в HTML, легко сможет додуматься, что к чему в этом сценарии.
Цикл с предусловием while
Эта конструкция также унаследована непосредственно от Си. Ее предназначение — цикличное выполнение команд в теле цикла, включающее предварительную проверку, нужно ли это делать (истинно ли логическое выражение в заголовке). Если не нужно (выражение ложно), то конструкция заканчивает свою работу, иначе выполняет очередную итерацию и начинает все сначала. Выглядит цикл так:
while(логическое_выражение) инструкция;
где, как обычно, логическое_выражение — логическое выражение, а инструкция — простая или составная инструкция тела цикла. (Очевидно, что внутри последнего должны производиться какие-то манипуляции, которые будут иногда изменять значение нашего выражения, иначе оператор зациклится. Это может быть, например, простое увеличение некоего счетчика, участвующего в выражении, на единицу.) Если выражение с самого начала ложно, то цикл не выполнится ни разу. Например:
$i=1; $p=1; while($i<32) {
echo $p," ";
$p=$p*2; // можно было бы написать $p*=2
$i=$i+1; // можно было бы написать $i+=1 или даже $i++
}
Данный пример выводит все степени двойки до 31-й включительно.
Как и инструкция if, цикл while имеет альтернативный синтаксис, что упрощает его применение вперемешку с HTML-кодом:
while(логическое_выражение): команды;
endwhile;
156 |
Часть III. Основы языка PHP |
Цикл с постусловием do-while
В отличие от цикла while, этот цикл проверяет значение выражения не до, а после каждого прохода. Таким образом, тело цикла выполняется хотя бы один раз. Выглядит оператор так:
do {
команды;
} while(логическое_выражение);
После очередной итерации проверяется, истинно ли логическое_выражение, и, если это так, управление передается вновь на начало цикла, в противном случае цикл обрывается.
Альтернативного синтаксиса для do-while разработчики PHP не предусмотрели (видимо, из-за того, что, в отличие от прикладного программирования, этот цикл довольно редко используется при программировании сценариев).
Универсальный цикл for
Я не зря назвал его универсальным — ведь с его помощью можно (и нужно) создавать конструкции, которые будут выполнять действия совсем не такие тривиальные, как простая переборка значения счетчика (а именно для этого используется for в Паскале и чаще всего в Си). Формат конструкции такой:
for(инициализирующие_команды; условие_цикла; команды_после_прохода) тело_цикла;
Работает он следующим образом. Как только управление доходит до цикла, первым делом выполняются операторы, включенные в инициализирующие_команды (слева направо). Эти команды перечисляются там через запятую, например:
for($i=0,$j=10,$k="Test!; ......)
Затем начинается итерация. Первым делом проверяется, выполняется ли условие_цикла (как в конструкции while). Если да, то все в порядке, и цикл продолжается. Иначе осуществляется выход из конструкции. Например:
// прибавляем по одной точке |
|
for($i=0,$j=0,$k="Test"; $i<10; |
.....) $k.="."; |
Предположим, что тело цикла проработало одну итерацию. После этого вступают в действие команды_после_прохода (их формат тот же, что и у инициализирующих операторов). Например:
for($i=0,$j=0,$k="Points"; $i<100; $j++,$i+=$j) $k=$k.".";
Хочется добавить, что приведенный пример (да и вообще любой цикл for) можно реализовать и через while, только это будет выглядеть не так изящно и лаконично. Например:
Глава 9. Конструкции языка |
157 |
$i=0; $j=0; $k="Points"; while($i<100) {
$k.="."; $j++; $i+=$j;
}
Вот, собственно говоря, и все... Хотя нет. Попробуйте угадать: сколько точек добавится в конец переменной $k после выполнения цикла?
Как обычно, имеется и альтернативный синтаксис конструкции:
for(инициализирующие_команды; условие_цикла; команды_после_прохода): операторы;
endfor;
Инструкции break и continue
Продолжим разговор про циклические конструкции. Очень часто для того, чтобы упростить логику какого-нибудь сложного цикла, удобно иметь возможность его прервать в ходе очередной итерации (к примеру, при выполнении какого-нибудь особенного условия). Для этого и существует инструкция break, которая осуществляет немедленный выход из цикла. Она может задаваться с одним необязательным параметром — числом, которое указывает, из какого вложенного цикла должен быть произведен выход. По умолчанию используется 1, т. е. выход из текущего цикла, но иногда применяются и другие значения:
for($i=0; $i<10; $i++) { for($j=0; $j<10; $j++) {
If($A[$i]==$A[$j]) break(2);
}
}
if($i<10) echo 'Найдены совпадающие элементы в матрице \$A!';
В этом примере инструкция break осуществляет выход не только из второго, но и из первого цикла, поскольку указана с параметром 2.
Применение такой формы записи break — новинка PHP версии 4. Честно го- воря, я не встречал ни одного другого языка, который бы использовал подоб- ный (на мой взгляд, крайне удачный) синтаксис. Спасибо вам, разработчики
PHP!
Инструкцию break удобно использовать для циклов поисков: как только очередная итерация цикла удовлетворяет поисковому условию, поиск обрывается. Например, вот цикл, который ищет в массиве $A первый нулевой элемент:
158 |
Часть III. Основы языка PHP |
for($i=0; $i<count($A); $i++) if($A[$i]==0) break;
if($i<count($A)) echo "Нулевой элемент найден: i=$i";
Стандартная функция count(), которую мы еще не рассматривали, просто возвращает число элементов в массиве $A.
Инструкция continue так же, как и break, работает только "в паре" с циклическими конструкциями. Она немедленно завершает текущую итерацию цикла и переходит к новой (конечно, если выполняется условие цикла для цикла с предусловием). Точно так же, как и для break, для continue можно указать уровень вложенности цикла, который будет продолжен по возврату управления.
В основном continue позволяет вам сэкономить количество фигурных скобок в коде и увеличить его удобочитаемость. Это чаще всего бывает нужно в циклах-фильтрах, когда требуется перебрать некоторое количество объектов и выбрать из них только те, которые удовлетворяют определенным условиям. Например, вот цикл, который обнуляет те элементы массива $A, которые удовлетворяют нескольким условиям:
for($i=0; $i<count($A); $i++) { if(!условие1($A[$i])) continue;
. . .
if(!условиеN($A[$i])) continue; $A[$i]=0;
}
Грамотное использование break и continue — искусство, позволяющее за- метно улучшить "читабельность" кода и количество блоков else. Возможно, в приведенных выше примерах оно и не было абсолютно оправданным, но, я уверен, рано или поздно вам придется столкнуться с ситуацией, когда без этих инструкций не обойтись.
Нетрадиционное использование do-while и break
Есть один интересный побочный эффект, который дает нам инструкция break, и который довольно удобно использовать для обхода "лишних" операторов (кстати, его можно применять и в Си). Необходимость такого обхода возникает довольно часто, причем именно при программировании сценариев. Рассмотрим соответствующий пример (листинг 9.2):
Листинг 9.2. Модель сценария для обработки формы
Глава 9. Конструкции языка |
159 |
. . .
$WasError=0; // индикатор ошибки — если не 0, то была ошибка // Если нажали кнопку Submit (с именем $doSubmit)...
if(@$doSubmit) do {
// Проверка входных данных
if(неправильное имя пользователя) { $WasError=1; break; }
. . . и т. д.
if(неправильные данные) { $WasError=1; break; }
. . . и т. д.
// Данные в порядке. Обрабатываем их. выполняем действия; выводим результат; завершаем сценарий;
} while(0);
. . .
Выводим форму, через которую пользователь будет запускать этот сценарий, и, возможно, отображаем сообщение об ошибке в случае, если $WasError!=0.
Здесь представлен наиболее обычный способ для организации сценариев-диалогов. Запустив сценарий без параметров, пользователь видит форму с приглашением ввести свое имя, пароль и некоторые другие данные. При нажатии кнопки запускается тот же самый сценарий, который определяет, что была нажата кнопка doSubmit, и первым делом проверяет имя и пароль. Если они заданы неверно, то отображается опять наша форма (и где-нибудь красным цветом сообщение об ошибке), в противном случае сценарий завершается и выдает страницу с результатом.
Мы видим, что указанный алгоритм можно реализовать наиболее удобно, имея ка- кой-то способ обрывания блока "проверки-и-завершения" и возврата к выводу формы заново. Как раз это и делает конструкция
if(что_то) do { ... } while(0);
Очевидно, что тело цикла do-while выполняется в любом случае только один раз (так как выражение в while всегда ложно). Тем не менее, такой "вырожденный" цикл мы можем использовать для быстрого выхода из него посредством break.
Многие сразу возразят, что в таких случаях удачнее будет задействовать функции и оператор return. Однако в PHP как раз это довольно неудобно, поскольку для того, чтобы из функции добраться до глобальной переменной (коей является любой элемент формы), нужно проделать несколько дополнительных шагов. Это, конечно, недостаток PHP, и о нем мы поговорим чуть позже.
160 |
Часть III. Основы языка PHP |
Цикл foreach
Данный тип цикла предназначен специально для перебора всех элементов массива и был добавлен только в четвертой версии языка PHP. Выглядит он следующим образом:
foreach(массив as $key=>$value)
команды;
Здесь команды циклически выполняются для каждого элемента массива, при этом очередная пара ключ=>значение оказывается в переменных $key и $value. Давайте рассмотрим пример (листинг 9.3), где покажем, как мы можем отобразить содержимое всех глобальных переменных при помощи foreach:
Листинг 9.3. Вывод всех глобальных переменных
<?
foreach($GLOBALS as $k=>$v)
echo "<b>$k</b> => <tt>$v</tt><br>\n"; ?>
У цикла foreach имеется и другая форма записи, которую следует применять, когда нас не интересует значение ключа очередного элемента. Выглядит она так:
foreach(массив as $value)
команды;
В этом случае доступно лишь значение очередного элемента массива, но не его ключ. Это может быть полезно, например, для работы с массивами-списками.
Цикл foreach оперирует не исходным массивом, а его копией. Это означает, что любые изменения, которые вносятся в массив, не могут быть "видны" из тела цикла. Что позволяет, например, в качестве массива использовать не только переменную, но и результат работы какой-нибудь функции, возвра- щающей массив (в этом случае функция будет вызвана всего один раз — до начала цикла, а затем работа будет производиться с копией возвращенного значения).
В следующей главе мы рассмотрим ассоциативные массивы и все, что к ним относится, гораздо более подробно.