Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель по PHP 4

.pdf
Скачиваний:
82
Добавлен:
02.05.2014
Размер:
4.36 Mб
Скачать

Глава 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 оперирует не исходным массивом, а его копией. Это означает, что любые изменения, которые вносятся в массив, не могут быть "видны" из тела цикла. Что позволяет, например, в качестве массива использовать не только переменную, но и результат работы какой-нибудь функции, возвра- щающей массив (в этом случае функция будет вызвана всего один раз до начала цикла, а затем работа будет производиться с копией возвращенного значения).

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