Скачиваний:
18
Добавлен:
02.05.2014
Размер:
906.24 Кб
Скачать

4. Работа с данными формы

Пожалуй, нет другого такого языка, как PHP, который бы настолько облегчил нам задачу обработки и разбора форм, поступивших из браузера. Дело в том, что в язык на самом нижнем уровне встроены все необходимые возможности, так что нам не придется даже и задумываться над особенностями протокола HTTP и размышлять, как же происходит отправка и прием POST-форм или даже загрузка файлов. Разработчики PHP все предусмотрели.

Формы

Как нам сделать, чтобы пользователь мог в удобной форме ввести свое имя и возраст? Очевидно, нам придется создать что-нибудь типа диалогового окна Windows, только в браузере. Итак, нам понадобится обычный HTML-документ (например, по имени form.html в корневом каталоге) с элементами этого диалога – текстовыми полями – и кнопкой. Давайте напишем сценарий, который будет обрабатывать данные формы (листинг 4.1).

Листинг 4.1. form.html: страница с формой

<html><body>

<form action=hello.php>

Введите имя: <input type=text name="name" value="Неизвестный"><br>

Введите возраст: <input type=text name="age" value="неопределенный"><br>

<input type=submit value="Нажмите кнопку, чтобы запустить сценарий!">

</form>

</body></html>

Загрузим наш документ в браузер. Теперь, если ввести в поле с именем свое имя, а в поле для возраста – свой возраст и нажать кнопку, браузер автоматически обратится к сценарию hello.php и передаст через ? все атрибуты, расположенные внутри тэгов <input> в форме и разделенные символом & в строке параметров. Заметьте, что в атрибуте action тэга <form> мы задали относительный путь, т. е. сценарий hello.php будет искаться браузером в том же самом каталоге, что и файл form.html.

Как мы знаем, все перекодирования и преобразования, которые нужны для URL-кодирования данных, осуществляются браузером автоматически. В частности, буквы кириллицы превратятся в %XX, где XX – некоторое шестнадцатеричное число, обозначающее код символа.

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

Листинг 4.2. hello.php — модель простого PHP-сценария

<html><body>

<?

получаем в $name имя из параметров, а в $age — возраст

echo "Привет, $name!<br> Я знаю, Вам $age лет!";

?>

</html></body>

Осталось теперь только определиться, как мы можем извлечь $name и $age из строки параметров. Конечно, мы можем попытаться разобрать ее "вручную" при помощи стандартных функций работы со строками (которых в PHP великое множество), и этот прием действительно будет работать. Однако, прежде чем браться за ненужное дело, давайте посмотрим, что нам предлагает сам язык.

Трансляция полей формы в переменные

Итак, мы не хотим заниматься прямым разбором переменной окружения QUERY_STRING, в которой хранятся параметры сценария. И правильно не хотим – интерпретатор перед запуском сценария делает все сам. Причем независимо от того, каким методом – GET или POST – воспользовался "браузер". То есть, PHP сам определяет, какой метод был задействован (благо, информация об этом доступна через переменную окружения REQUEST_METHOD), и получает данные либо из QUERY_STRING, либо из стандартного входного потока. Это крайне удобно и достойно подражания, вообще говоря, в любых CGI-сценариях. А именно, интерпретатор все данные из полей формы преобразует в глобальные одноименные переменные. В нашем случае значение поля name после начала работы программы будет храниться в переменной $name, а значение поля age – в переменной $age. То есть, не надо ничего ниоткуда "получать" – все уже установлено и распаковано из URL-кодировки. Максимум удобств, минимум затрат, не правда ли? К тому же, еще и работает быстрее, чем аналогичный кустарный код, написанный на PHP, потому что разработчики PHP предусмотрели функцию разбора командной строки на Си. Вот наш окончательный сценарий hello.php (листинг 4.3).

Листинг 4.3. hello.php: окончательная версия

<html><body>

<? echo "Привет, $name!<br> Я знаю, Вам $age лет!" ?>

</html></body>

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

Листинг 4.4. hello.php: усовершенствованная версия

<html><body>

<?if($doGo) {?>

<form action="<?=$SCRIPT_NAME?>">

Введите имя: <input type=text name="name"><br>

Введите возраст: <input type=text name="age"><br>

<input type=submit name="doGo" value="Нажмите кнопку!">

</form>

<?} else {?>

Привет, <?=$name?>!<br>

Я знаю, Вам <?=$age?> лет!"

<?}?>

</html></body>

Из этого примера мы можем почерпнуть еще один удобный прием, который нами пока не рассматривался. Это конструкция <?=выражение?>. Она является ничем иным, как просто более коротким обозначением для <?echo(выражение)?>, и предназначена для того, чтобы вставлять величины прямо в HTML-страницу.

Помните наши рассуждения о том, что же первично в 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: его код встроен в сам сценарий. Именно так и нужно разрабатывать сценарии: и просто и делу польза. Здесь действует общий принцип: чем меньше файлов, задающих внешний вид страницы, тем лучше (только не обобщайте это на файлы с программами – последствия могут быть катастрофическими!).

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

Механизм трансляции полей формы в 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

</select>

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

аrray (0=>"First", 1=>"Third").

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

$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 совершенно нет никакого дела до того, в каких элементах формы мы используем автомассивы – он в любом случае обрабатывает все одинаково. И это, пожалуй, правильно.

Трансляция массивов

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

Листинг 4.5. Трансляция массивов

Имя: <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.

Если верить официальной документации, то многомерные массивы (то есть, массивы массивов) указывать нельзя. Например, при передаче данных поля, определенного как <inputtype=textname=Silly[one][two][three]> в программе, действительно, создастся массив $Silly, но он будет одномерный и с ключом one][two][three – совсем не то, что мы ожидали, не правда ли? В принципе, при большом желании можно написать функцию, которая конвертирует такие "испорченные" массивы в нормальное многомерное представление, но это выходит за рамки нашего обзора. К счастью, похоже, разработчики PHP поняли, что неработоспособность многомерных массивов при передаче их из формы серьезно снижает популярность PHP. Поэтому они наконец-то включили в PHP поддержку последних. Впрочем, в документации по-прежнему заявлено, что "многомерные массивы использовать нельзя". Что это – ошибка или злая шутка?..Как же проверить, можно ли использовать многомерные массивы при обработке форм в вашей версии PHP? Нет ничего проще! Достаточно запустить следующий сценарий (листинг 4.6).

Листинг 4.6. 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. В этом случае мы получим сообщение, что массивами пользоваться можно, а иначе – что они не работают.