### Лабораторная работа 3. Обработка форм

1 Теоретический материал
2 Конторольные вопросы
3 Задание

### 1 Теоретический материал

В web-пространстве для передачи данных от посетителя web-страницы (мы будем также называть такого гипотетического человека пользователем) на сервер используются HTML-формы. В языке РНР предусмотрены многочисленные средства для работы с формами.

Программа для обработки ввода пользователя

До некоторого времени мы будем разделять HTML-текст и текст РНР-программ. В листинге 10.1 приведен пример простой HTML-формы.

Листинг 10.1. Простая HTML-форма

<html> <head>
<title> Листинг 10-1. Простая HTML-форма </title>
</head> <body>

<!--
Форма для задания цвета через переменную $bg.
Ее использование — в скрипте ls10-2.php
-->

<p><b>Выберите цвет:</b>

<form action="ls10-2.php" method="GET">
<p><input type="radio" name="bg" value="red">red
<p><input type="radio" name="bg" value="green">green
<p><input type="radio" name="bg" value="blue">blue
<p><input type="submit" value="Выполнить">
</form>

</body> </html>

Мы создали форму, в которой есть набор кнопок-переключателей (radio button) с общим именем "bg" и кнопка передачи данных "submit". Атрибут action тега form указывает на файл ls10-2.php, следовательно этот файл будет обрабатывать данные формы. Поскольку мы указали здесь только имя файла, а путь к нему опустили, то файл должен находиться в том же каталоге на сервере, что и файл с программой 10.1.

Обратите внимание, как передается значение переменной в методе GET (т.е. что пишется в поле «Адрес» браузера при нажатии кнопки «Выполнить») — это строка вида

ls10-2.php?bg=red

т.е. после имени выполняемого скрипта идет знак вопроса, затем имя передаваемой переменной, знак «равно» и значение этой переменной.

В листинге 10.2 приведена программа, обрабатывающая данные формы из листинга 10.1.

Листинг 10.2. Чтение данных формы из листинга 10.1

<html> <head>
<title> Листинг 10-2. Чтение данных формы
из листинга 10-1 </title> </head> <body>

<?php

/* Из файла ls10-1.php предается переменная $bg с названием цвета. Здесь она используется для задания цвета посещенной гиперссылки и цвета фона маленькой таблицы (прямоугольника)
*/

print "<body bgcolor=silver text=black link=white
alink=maroon vlink=$bg>\n";

print "<p><table border=1 width=100 align=center>\n";
print "<tr> <td bgcolor=$bg>&nbsp;<br>&nbsp;</td>
</tr></table>\n";

print "<p align=center><a href='ls10-1.php'>назад</a>";

?>
</body> </html>

Эта программа запускается не щелчком на ссылке и не указанием имени файла в строке адреса в окне браузера. Текст программы из листинга 10.2 нужно поместить в файл с именем ls10-2.php, и этот файл будет запускаться сервером тогда, когда пользователь нажмет кнопку «Выполнить» в форме из листинга 10.1. В программе мы обращаемся к переменной $bg — в ней записано название цвета, которое выбрал пользователь. В результате на экране будет изображен прямоугольник и гиперссылка «назад» соответствующего цвета. Обработка форм в языке РНР действительно настолько проста. Все данные, переданные пользователем, становятся доступными программе в виде глобальных переменных, имена которых совпадают с именами соответствующих элементов HTML-формы. Однако, будьте внимательны, если в файле настроек языка PHP php.ini директива register_g lobals=Off, т.е. отключена, то так просто передавать переменные из скрипта в скрипт не удастся! В этом случае придется использовать специальные переменные окружения.

Обработка элементов с многозначным выбором

В предыдущем примере мы рассмотрели обработку данных, при которой каждому элементу соответствует только одно значение. Но остается неясным, как обрабатывать информацию от элементов, которые позволяют выбирать несколько значений из списка. Создав несколько флажков с одним именем, вы позволяете пользователю ввести несколько значений под этим одним именем. Если в теге input мы дадим этому элементу простое имя, то по такому имени программа сможет обратиться только к одному значению, связанному с этим элементом. Для того чтобы программа могла иметь доступ ко всем значениям, выбранным пользователем, нужно к имени данного элемента приписать пару пустых квадратных скобок (т.е. создать массив). Это и сделано в листинге 10.3.

Листинг 10.3. HTML-форма с выбором из списка

<html> <head>
<title> Листинг 10-3. HTML-форма с выбором из списка
</title> </head> <body>

<!--
Здесь в скрипт ls10-4.php передаются:
1) переменная $user
2) массив hobby[] со значениями, которые были выбраны
в форме
-->

<form action="ls10-4.php" method="POST">

<p>Введите ваше имя

<p><input type="text" name="user">

<p>Что вы любите делать в свободное время <br>
(можно выбрать несколько вариантов)

<p><input type="checkbox" name="hobby[]"
value="слушать музыку">слушать музыку

<p><input type="checkbox" name="hobby[]"
value="читать книгу">читать книгу

<p><input type="checkbox" name="hobby[]"
value="смотреть телевизор">смотреть телевизор

<p><input type="checkbox" name="hobby[]"
value="гулять на улице">гулять на улице

<p><input type="submit" value="Выбор сделан">

</form>
</body> </html>

Теперь в программе ls10-4.php, предназначенной для обработки этой формы, значения, выбранные пользователем в элементе "hobby[]", будут доступны как элементы массива $hobby. Кроме того, в программу
ls10-4.php передается значение переменной $user — в которой записано то, что пользователь указал в текстовом поле "user". Пример вы видите в листинге 10.4.

Листинг 10.4. Обработка данных формы из листинга 10.3

<html> <head>
<title> Листинг 10-4. Обработка данных формы
из листинга 10-3 </title> </head> <body>

<?php

print "<p>$user, оказывается, вы предпочитаете";

print "<ul>\n";

foreach ($hobby as $value)
{print "<li>$value\n";
}
print "</ul>\n";

?>
</body> </html>

Несколько флажков с одним именем — не единственный способ, который позволяет ввести несколько значений. В листинге 10.3 можно заменить последовательность флажков с одним именем на тег SELECT с атрибутом multiple, и работать это будет точно так же.

<select name= "hobby[]" multiple>

<option value="лежать на диване">лежать на диване
<option value="читать книгу">читать книгу
<option value="смотреть телевизор">смотреть телевизор
<option value="гулять на улице">гулять на улице
</select>

Доступ ко всем полям формы через ассоциативный массив

Рассмотренная выше техника работает прекрасно, но только в том случае, если вы заранее знаете имена всех полей формы. Однако вам может понадобиться написать программу, которая умеет настраиваться на изменения, сделанные в форме, или даже обрабатывать несколько разных форм, при этом не заботясь о фактических именах полей. Эту проблему можно решить с помощью глобальных переменных РНР. В зависимости от того, используете ли вы метод передачи GET (короткая строка с данными) или POST (большой объем данных), в ваше распоряжение предоставляется один из массивов — $HTTP_GET_VARS или $НТТР_POST_VARS. Это ассоциативные массивы, содержащие пары имя/значение для каждого элемента переданной формы. В листинге 10.5 показано, как с помощью этого способа можно вывести список всех полей и переданных значений при использовании метода передачи POST.

Листинг 10.5. Чтение данных произвольной формы
с помощью ассоциативных массивов

<?php

/* Заменить цикл foreach в листинге ls10-4.php
Листинг 10-5. Чтение данных произвольной формы
с помощью ассоциативных массивов
*/

foreach ($HTTP_POST_VARS as $key=>$value) {
print "$key = $value<br>\n";
}
?>

Приведенная выше программа выводит имена и значения всех параметров, переданных ей. Конечно, не всегда разумно обрабатывать данные таким образом. Если бы мы передали этой программе для обработки форму из листинга 10.3, то получили бы следующее:

user = guest
hobby = Array

Дело в том, что данные hobby существуют как элементы массива $НТТР_POS T_VARS [hobby], а мы попытались обратиться к ним как к простой переменной. В листинге 10.6 эта ошибка исправлена и, перед тем как вывести те или иные данные, их тип проверяется.

Листинг 10.6. Чтение данных произвольной формы
с проверкой типов

<?php

/* Заменить цикл foreach в листинге ls10-4.php
Листинг 10-6. Чтение данных произвольной формы
с проверкой типов
*/

foreach ($HTTP_POST_VARS as $key=>$value)
{ #1
if (gettype($value) == "array")
{ #2
print "$key = <br>\n";
foreach ($value as $v ) {print "$v<br>";}

} #2

else {
print "$key = $value<br>\n";
}
} #1
?>

Как и в предыдущих примерах, в цикле foreach мы просматриваем массив $HTTP_POS T_VARS и с помощью функции gettype() проверяем, является ли очередная переменная массивом. Если да, то создаем вложенный цикл, в котором выводим значения этого массива.

Определение метода передачи

Если необходимо, чтобы программа работала правильно и при этом могла обрабатывать данные, полученные от любой формы, то программа должна определять, по какому методу ей переданы данные, и в зависимости от этого читать ассоциативный массив $HTTP_GET_VARS или $HTTP_POST_VARS. В большинстве систем вы можете определить, какой метод использовался для передачи, просмотрев значение переменной среды $REQUEST_METHOD, в которой будет записано POST или GET. Кроме того, есть еще один способ узнать используемый метод, если помнить о том, что массив $HTTP_POST_VARS существует только при использовании метода POST.

Пример того, как это сделать, приведен в листинге 10.7.

Листинг 10.7. Независимое от метода чтение данных

<?php

/* Заменить цикл foreach в листинге ls10-4.php
Листинг 10-7. Независимое от метода чтение данных
*/

$PARAMS = (isset($HTTP_POST_VARS))

? $HTTP_POST_VARS : $HTTP_GET_VARS;

foreach ($PARAMS as $key=>$value)
{ #1

if (gettype($value) == "array")
{ #2
print "$key = <br>\n";

foreach ($value as $v)
print "$v<br>";
} #2

else
{print "$key = $value<br>\n";
}
} #1
?>

С помощью тернарного логического оператора мы устанавливаем переменную $PARAMS. Потом, используя функцию isset(), проверяем, определено ли значение массива $HTTP_POST_VARS. Данная функция возвращает значение true, если переданная ей переменная определена. После этого можно пользоваться массивом $PARAMS, независимо от того, в результате какого метода были определены его значения.

Расположение HTML-текста и РНР-программы
на одной странице

При некоторых обстоятельствах вам может понадобиться расположить РНР-программу, обрабатывающую данные, на той же странице, что и форму, передающую такие данные. Это может оказаться удобным, например, в том случае, когда вам нужно вывести форму несколько раз. В листинге 10.8 приведен пример такой формы: на экране браузера изображается небольшой прямоугольник — таблица, содержащая всего одну ячейку и предлагается задать цвет для этого прямоугольника. После выбора нужного цвета и нажатия кнопки "закрасить", можно видеть, что прямоугольник тут же меняет свой цвет.

Листинг 10.8. HTML-форма, вызывающая саму себя

<html> <head>
<title> Листинг 10-8. HTML-форма, вызывающая саму себя
</title> </head> <body>

<?php

if (!isset ($bg)) {$bg = "silver";}

print "<p>Выберите цвет фона прямоугольника";
print "<p><table border=1 width=100>";
print "<tr> <td bgcolor=$bg>&nbsp;<br>&nbsp;
</td></tr></table>";
?>

<form action="<? print $PHP_SELF ?>" method="get">

<p><input type="radio" name="bg" value="red">красный
<p><input type="radio" name="bg" value="green">зеленый
<p><input type="radio" name="bg" value="blue">синий
<p><input type="radio" name="bg" value="silver">исходный

<p><input type="submit" value="закрасить">

</form>

</body> </html>

Как бы мы ни назвали файл, содержащий эту форму, все равно форма будет обращаться к этому файлу, так как в ней в атрибуте ACTION есть переменная $PHP_SELF. Функция isset() возвращает истину, если указанная переменная уже была определена (ей было присвоено значение), тогда "!isset()" возвратит истину, если указанная переменная не определена т.е.когда мы первый раз вызываем файл с формой.

Динамическое конструирование форм

До настоящего момента мы программировали все формы вручную, поскольку точно знали количество элементов формы. А что делать, если требуется вывести, например, раскрывающийся список, элементы которого хранятся в массиве, размер которого нам заранее неизвестен.

Предположим, вы хотите дать возможность посетителю выбрать один из поисковых серверов и автоматически перенаправить его туда. В листинге 10.9 для этого используется функция header(). Когда программа на сервере общается с клиентом, она должна сначала послать некоторые заголовки, содержащие информацию о том документе, который за ними следует. Обычно РНР делает это автоматически, но вы можете послать свой собственный заголовок с помощью функции header(). Но прежде чем послать заголовок, вы должны убедиться в том, что никаких данных от вашей программы на браузер пока не поступало. К тому моменту, когда данные посылаются на браузер, заголовки уже отправлены и вам уже поздно посылать свой собственный заголовок. Даже простой перевод строки или единственный пробел приводят к тому, что программа посылает заголовок типа содержания. Если вы хотите послать свой собственный заголовок с помощью функции header(), то должны проверить программу и убедиться, что ничего пока не отправлено.

Послав браузеру заголовок "Location", вы перенаправите пользователя на новую страницу:

header ("Location: http://www.my_new_site.ru");

Листинг 10.9. Посылка заголовка с помощью функции header()

<?php
if ($day != "") {
header("Location: $day");
exit;
}

else { // начало блока else
?>

<html> <head>
<title> Листинг 10-9. Посылка заголовка с помощью
функции header() </title> </head> <body>

<form action = "<? print $PHP_SELF ?>" method="post">

<select name="day">
<option value = "">Дни недели:

<option value='d1.htm'>Понедельник

<option value='d2.htm'>Вторник

<option value='d3.htm'>Среда

</select>
<input type="submit" value="Перейти">
</form>

<?php
} // конец блока else
?>
</body> </html>

Сначала мы проверяем, было ли присвоено значение переменной $day, являющейся именем элемента select в форме. Если проверка дает положительный результат, вызывается функция header() с аргументом, в котором значение переменной $day присоединяется к строке "Location: ". При передаче этой команды функция header() перенаправляет браузер на соответствующий файл (d 1.htm, d 2.htm или d 3.htm). Если значение переменной $day, не задано, форма выводится в браузере.

### 2 Конторольные вопросы

1.Каким образом данные HTML-формы, переданные пользователем, становятся доступными PHP-программе?

2.Как обрабатывать информацию от элементов, которые позволяют выбирать несколько значений из списка?

3.Опишите способы, позволяющие узнать используемый метод передачи данных.

4.Как создать HTML-форму, вызывающую саму себя?

5.Для чего используется функция header() ?

### 3 Задание

### № 1

Создайте файл z4-1a.htm с HTML-формой, позволяющей выбрать
а) горизонтальное (align, варианты значений: left, center, right),
б) вертикальное (valign, варианты значений: top, middle, bottom)
расположение текста в таблице.
Для (а) использовать радиокнопки, для (б) - флажки.
Обработка формы выполняется в скрипте z4-1b.php по нажатии кнопки "Выполнить".

В скрипте z4-1b.php сформировать таблицу, состоящую из одной ячейки шириной и высотой 100 пикселов, атрибуты ячейки align и valign должны получить значения, переданные из формы. В ячейку таблицы поместить слово "Текст". Под таблицей вставить гиперссылку на файл z4-1a.htm ("Назад").

(Использовать листинги 10-1 и 10-2).

### № 2

На основе предыдущего задания создайте файл z4-2.php с HTML-формой, которая вызывает сама себя. По умолчанию установите такие значения: для align - "left", для valign - "top".

(Использовать листинг 10-8).

### № 3

Создайте файл z4-3a.htm с HTML-формой "Гoрoдa и памятники" для тестирования знаний. В этой форме нужно ввести имя тестируемого, а затем для каждого памятника выбрать, в каком городе он находится. Выбор городов - раскрывающийся список. Список вопросов и ответов - в файле vopr.txt.

Обработка формы выполняется в скрипте z4-3b.php, в котором необходимо создать массив $otv с номерами правильных ответов (см. файл vopr.txt) и сделать вывод на экран имени тестируемого и оценки его знаний в зависимости от количества правильных ответов (см. файл vopr.txt, использовать оператор switch).

(Использовать листинги 10-3 и 10-4).

### № 4

Листинг 10-3 последовательно сохранить как z4-45.php, z4-46.php, z4-47.php для того, чтобы выполнились листинги 10-5, 10-6 и 10-7 (ls10-5.php, ls10-6.php и ls10-7.php).

### № 5

На основе листинга 10-9 создайте скрипт z4-5.php в котором сперва проверяется, было ли присвоено значение переменной $site. Если проверка дает положительный результат, осуществляется перенаправление пользователя на сайт поисковой системы, адрес которого - значение переменной $site. Если же значение переменной $site не задано, выводится HTML-форма с раскрывющимся списком поисковых сайтов:

Эти сайты должны быть занесены в массив $list_sites[]. Предполагается, что данный массив может дополняться, а значит его размер заранее неизвестен, поэтому раскрывающийся список с адресами поисковых систем сформировать с помощью цикла while и функции, возвращающей количество элементов массива.