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

Самоучитель PHP 4 - Котеров Д. В

..pdf
Скачиваний:
92
Добавлен:
24.05.2014
Размер:
4.38 Mб
Скачать

148

Часть III. Основы языка PHP

тать, что первичен текст. В нашем примере именно так и происходит ведь кода на PHP тут очень мало, в основном страница состоит из HTML-тэгов.

Обратите внимание на полезный прием: в параметре action тэга <form> мы не задали явно имя файла сценария, а извлекли его из переменной SCRIPT_NAME (которая устанавливается автоматически перед запуском сценария). Это позволило нам не "привязываться" к имени файла, т. е. теперь мы можем его в любой момент переименовать без потери функциональности.

Если PHP установлен не как модуль Apache, а как отдельный обработчик, то переменная $SCRIPT_NAME будет содержать не то значение, на которое мы рассчитываем. Например, если воспользоваться способом инсталляции PHP, который предлагается во второй части этой книги (когда мы устанавливаем PHP именно как внешнюю программу, а не модуль Apache), после запуска сце- нария переменная $SCRIPT_NAME будет содержать строку /_php/php.exe, что, конечно же, нам не подходит. "Правильное" значение в этом случае можно найти в переменной окружения REDIRECT_URL, или в переменной PHP $REDIRECT_URL.

К тому же, теперь исчезла необходимость и в промежуточном файле form.html: его код встроен в сам сценарий. Именно так и нужно разрабатывать сценарии: и просто и делу польза. Здесь действует общий принцип: чем меньше файлов, задающих внешний вид страницы, тем лучше (только, ради бога, не обобщайте это на файлы с программами — последствия могут быть катастрофическими!).

Трансляция переменных окружения и Cookies

Однако "интеллектуальные" возможности PHP на этом далеко не исчерпываются. Дело в том, что в переменные преобразуются не только все данные формы, но и переменные окружения (включая QUERY_STRING, CONTENT_LENGTH и многие другие), а также все Cookies.

Например, вот сценарий (листинг 8.6), который печатает IP-адрес пользователя, который его запустил, а также тип его браузера (эти данные хранятся в переменных окружения REMOTE_USER и HTTP_USER_AGENT):

Листинг 8.6. Вывод IP-адреса и браузера пользователя

<html><body>

Ваш IP-адрес: <?=$REMOTE_USER?><br> Ваш браузер: <?= HTTP_USER_AGENT?> </body></html>

Глава 8. Работа с данными формы

149

По умолчанию трансляция выполняется в порядке ENVIRONMENT-GET-POST- COOKIE, причем каждая следующая переменная как бы перекрывает предыдущее свое значение. Например, пусть у нас есть переменная окружения A=10, параметр, поступивший из GET-формы A=20 и Cookie A=30. В этом случае в переменную $A сценария будет записано 30, поскольку Cookie перекрывает GET, а GET перекрывает переменные окружения. Так что, проверяя какуюлибо переменную окружения VAR в сценарии (особенно если она касается вопросов, связанных с разграничением прав доступа — например, переменная содержит пароль), задумайтесь на минутку: а что, если злоумышленник запустит ваш сценарий вот так:

http://www.somehost.com/foo.php?VAR=что_то_очень_нехорошее

и старое значение переменной окружения VAR окажется стертым? К счастью, в таких ситуациях есть выход — достаточно воспользоваться функцией getenv(), чтобы прочитать значение переменной окружения с указанным именем, и только его — невзирая ни на какие другие данные. Подробнее об этой функции мы поговорим чуть позже.

Трансляция списков

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

<select multiple>:

<select name=Sel multiple> <option>First <option>Second <option>Third

</select>

В таком списке вы можете выбрать (подсветить) не одну, а сразу несколько строчек, используя клавишу <Ctrl> и щелкая по ним кнопкой мыши. Пусть мы выбрали First и Third. Тогда после отправки формы сценарию придет строка параметров Sel=First&Sel=Third, и в переменной $Sel окажется, конечно, только Third. Значит ли это, что первый пункт потерялся и механизм трансляции в PHP работает некорректно? Оказывается, нет, и для решения подобных проблем в PHP предусмотрена возможность давать имена полям формы в виде имени массива с индексами:

<select name="Sel[]" multiple> <option>First

<option>Second

<option>Third

150

Часть III. Основы языка PHP

</select>

Теперь сценарию придет строка Sel[]=First&Sel[]=Third, интерпретатор обнаружит, что мы хотим создать "автомассив" (то есть массив, который не содержит пропусков, и у которого индексация начинается с нуля), и, действительно, создаст переменную $Sel типа массив, содержимое которого следующее: array(0=>"First", 1=>"Third"). Как мы видим, в результате ничего не пропало — данные только слегка видоизменились.

Подробнее про ассоциативные массивы и автомассивы читайте в главе 10.

Все же, забегая вперед, еще несколько слов об автомассивах. Рассмотрим такой несложный пример программы:

$A[]=10; $A[]=20; $A[]=30;

После отработки этих строк будет создан массив $A, заполненный последовательно числами 10, 20 и 30, с индексами, отсчитываемыми с нуля. То есть, если внутри квадратных скобок при присваивании элементу массива не указано ничего, то подразумевается элемент массива, следующий за последним. В общем-то это должно быть интуитивно понятным — именно на легкость в использовании и ориентировались разработчики PHP.

Прием с автомассивом в поле <select multiple>, действительно, выглядит довольно элегантно. Однако не стоит думать, что он применим только к этому элементу формы: автомассивы мы можем применять и в любых других полях. Вот пример, создающий 2 переключателя (кнопки со значениями вкл/выкл), один редактор строки и одно текстовое (многострочное) поле, причем все данные после запуска сценария, обрабатывающего эту форму, будут представлены в виде одного-единственного автомассива:

<input type=checkbox name=Arr[] value=ch1> <input type=checkbox name=Arr[] value=ch2> <input type=text name=Arr[] value="Some string"> <textarea name=Arr[]>Some text</textarea>

То есть, мы видим, что PHP совершенно нет никакого дела до того, в каких элементах формы мы используем автомассивы — он в любом случае обрабатывает все одинаково. И это, пожалуй, правильно.

Глава 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 первый нулевой элемент: