Синтаксис: формы

HTML-формы. Массивы $_GET и $_POST

Рассмотрим пример:

<html>
<body>
<?
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
echo '<h1>Привет, <b>' . $_POST['name'] . '</b></h1>!';
}
?>
<form method="POST" action=/<span class="st0">"<?=$_SERVER['PHP_SELF']?>">
Введите Ваше имя: <input type="text" name="name">
<br>
<input type="submit" name="okbutton" value="OK">
</form>
</body>
</html>

Форма содержит два элемента: name и okbutton. Атрибут method указывает метод отправки формы POST, атрибут же action, указывающий URL, на который отправляется форма, заполняется значением серверной переменной PHP_SELF - адресом выполняемого в данный момент скрипта.

<?=$_SERVER['PHP_SELF']?> - сокращенная форма записи для echo: <? echo $_SERVER['PHP_SELF']; ?>.

Предположим, в поле name мы ввели значение Вася, и нажали кнопку OK. При этом браузер отправляет на сервер POST-запрос. Тело запроса: name=Вася&okbutton=OK. PHP автоматически заполняет массив $_POST:

$_POST['name'] = 'Вася'
$_POST['okbutton'] = 'OK'

В действительности, значение "Вася" отправляется браузером в urlencode-виде; для кодировки windows-1251 это значение выглядит как %C2%E0%F1%FF. Но, поскольку PHP автоматически осуществляет необходимое декодирование, мы можем "забыть" об этой особенности - пока не придется работать с HTTP-запросами вручную. Так как в теле запроса указываются только имена и значения, но не типы элементов форм, PHP понятия не имеет, соответствует $_POST['name'] строке ввода, кнопке, или списку. Но эта информация нам, в общем-то, совершенно не нужна. :)

Поскольку знать, что написано на кнопке submit, нам необязательно, в строке 11 можно удалить атрибут name, сократив описание кнопки до <input type="submit" value="OK">. В этом случае, браузер отправит POST-запрос name=Вася.

А теперь - то же самое, но для GET-формы:

<html>
<body>
<?
if (isset($_GET['name'])) {
echo '<h1>Привет, <b>' . $_GET['name'] . '</b></h1>!';
}
?>
<form action=/<span class="st0">"<?=$_SERVER['PHP_SELF']?>">
Введите Ваше имя: <input type="text" name="name">
<br>
<input type="submit" value="OK">
</form>
</body>
</html>

В строке 8 можно было бы с таким же успехом написать <form method="GET">: GET - метод по умолчанию. В этот раз браузер отправляет GET-запрос, который равносилен вводу в адресной строке адреса: http://адрес-сайта/имя-скрипта.php?name=Вася.

PHP с GET-формами поступает точно так же, как и с POST, с тем отличием, что заполняется массив $_GET.

Кардинальное же отличие - в строке 4. Поскольку простой ввод адреса в строке браузера является GET-запросом, проверка if ($_SERVER['REQUEST_METHOD'] == 'GET') бессмысленна: все, что мы в этом случае выясним - что кто-то от нефиг делать не отправил на наш скрипт POST-форму. ;) Поэтому мы прибегаем к конструкции isset(), которая возвращает true, если данная переменная определена (т.е. ей было присвоено значение), и false - если переменная не определена. Если форма была заполнена - как вы уже поняли, PHP автоматически присваивает $_GET['name'] соответствующее значение.

Cпособ проверки с помощью isset() - универсальный, его можно было бы использовать и для POST-формы. Более того, он предпочтительнее, так как позволяет выяснить, какие именно поля формы заполнены.

Во многих старых книгах и статьях утверждается, что:
  1. Данные как из GET, так и из POST-форм попадают непосредственно в переменные (в нашем случае - $name), причем POST-данные приоритетнее, т.е. "затирают" GET-данные;
  2. Также данные GET и POST-форм хранятся соответственно в массивах $HTTP_GET_VARS и $HTTP_POST_VARS.
Эта информация давным-давно устарела. Уже года три как. Web-программирование, и, в частности, PHP развивается быстрыми темпами. Запомните этот момент, чтобы не попасть впросак со старыми книгами или скриптами. Подробности - в статье "Суперглобальные массивы и register_globals".

Немного более сложный пример.

<html>
<body>
<?
if (isset($_POST['name'], $_POST['year'])) {
if ($_POST['name'] == '') {
echo 'Укажите имя!<br>';
} else if ($_POST['year'] < 1900 || $_POST['year'] > 2004) {
echo 'Укажите год рождения! Допустимый диапазон значений: 1900..2004<br>';
} else {
echo 'Здравствуйте, ' . $_POST['name'] . '!<br>';
$age = 2004 - $_POST['year'];
echo 'Вам ' . $age . ' лет<br>';
}
echo '<hr>';
}
?>
<form method="post" action=/<span class="st0">"<?=$_SERVER['PHP_SELF']?>">
Введите Ваше имя: <input type="text" name="name">
<br>
Введите Ваш год рождения: <input type="text" name="year">
<input type="submit" value="OK">
</form>
</body>
</html>

Никаких новых приемов здесь не используется. Разберитесь, выполните код, попробуйте модифицировать...

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

<html>
<body>
<?
$name = isset($_POST['name']) ? $_POST['name'] : '';
$year = isset($_POST['year']) ? $_POST['year'] : '';
 
if (isset($_POST['name'], $_POST['year'])) {
if ($_POST['name'] == '') {
echo 'Укажите имя!<br>';
} else if ($_POST['year'] < 1900 || $_POST['year'] > 2004) {
echo 'Укажите год рождения! Допустимый диапазон значений: 1900..2004<br>';
} else {
echo 'Здравствуйте, ' . $_POST['name'] . '!<br>';
$age = 2004 - $_POST['year'];
echo 'Вам ' . $age . ' лет<br>';
}
echo '<hr>';
}
?>
<form method="post" action=/<span class="st0">"<?=$_SERVER['PHP_SELF']?>">
Введите Ваше имя: <input type="text" name="name" value="<?=$name?>">
<br>
Введите Ваш год рождения: <input type="text" name="year" value="<?=$year?>">
<input type="submit" value="OK">
</form>
</body>
</html>

Несколько непонятными могут оказаться строки 4 и 5. Все очень просто: X = A ? B : C - сокращенная запись условия if (A) X=B else X=C. Строку 4 можно было бы записать так:

if (isset($_POST['name']))
$name = $_POST['name'];
else
$name = '';

Используемая же в строках 21 и 23 конструкция <?=$foo ?> - и того проще: это сокращение для <? echo $foo ?>.

Может возникнуть вопрос - почему бы не выбросить строки 4-5 и не написать:

Введите Ваше имя: <input type="text" name="name" value="<?=$_POST['name']?>"><br>
Введите Ваш год рождения: <input type="text" name="year" value="<?=$_POST['year']?>">

Дело в том, что, если эти POST-переменные не определены - а так и будет, если форму еще не заполняли, - PHP выдаст предупреждения об использовании неинициализированных переменных (причем, вполне обоснованно: такое сообщение позволяет быстро находить труднообнаружимые опечатки в именах переменных, а также предупреждает о возможных "дырах" на сайте). Можно, конечно, поместить код с isset... прямо в форму, но получится слишком громоздко.

Разобрались? А теперь попробуйте найти ошибку в приведенном коде. Ну, не совсем ошибку, - но недочет.

htmlspecialchars()

Не нашли? Я подскажу. Введите, например, в поле "имя" двойную кавычку и какой-нибудь текст, например, "123. Отправьте форму, и взгляните на исходный код полученной страницы. В четвертой строке будет что-то наподобие: Введите Ваше имя: <input type="text" name="name" value=""123">

То есть - ничего хорошего. А если бы хитрый пользователь ввел JavaScript-код?

Для решения этой проблемы необходимо воспользоваться функцией htmlspecialchars(), которая заменит служебные символы на их HTML-представление (например, кавычку - на "):

<html>
<body>
<?
$name = isset($_POST['name']) ? htmlspecialchars($_POST['name']) : '';
$year = isset($_POST['year']) ? htmlspecialchars($_POST['year']) : '';
 
if (isset($_POST['name'], $_POST['year'])) {
if ($_POST['name'] == '') {
echo 'Укажите имя!<br>';
} else if ($_POST['year'] < 1900 || $_POST['year'] > 2004) {
echo 'Укажите год рождения! Допустимый диапазон значений: 1900..2004<br>';
} else {
echo 'Здравствуйте, ' . $name . '!<br>';
$age = 2004 - $_POST['year'];
echo 'Вам ' . $age . ' лет<br>';
}
echo '<hr>';
}
?>
<form method="post" action=/<span class="st0">"<?=$_SERVER['PHP_SELF']?>">
Введите Ваше имя: <input type="text" name="name" value="<?=$name?>">
<br>
Введите Ваш год рождения: <input type="text" name="year" value="<?=$year?>">
<input type="submit" value="OK">
</form>
</body>
</html>

Повторите опыт и убедитесь, что теперь HTML-код корректен.

Запомните - функцию htmlspecialchars() необходимо использовать всегда, когда выводится содержимое переменной, в которой могут присутствовать спецсимволы HTML.

При некорректной с точки зрения строгих стандартов (но вполне приемлимой с точки зрения браузеров) верстке, когда вместо двойных кавычек используются одинарные (<input ... value='значение'>), htmlspecialchars() оставляет уязвимость, т.к. никак не обрабатывает одинарную кавычку. Для дополнительной замены одинарной кавычки на ее html entity-аналог следует указывать параметр ENT_QUOTES:
$name = isset($_POST['name']) ? htmlspecialchars($_POST['name'], ENT_QUOTES) : '';

Рекомендуется использовать этот параметр всегда: вреда не будет, а дополнительную защиту получаем.

phpinfo()

Функция phpinfo() - одна из важнейших в PHP. Она выводит информацию о настройках PHP, значения всевозможных конфигурационных переменных...

Почему я упоминаю о ней в главе, посвященной формам? phpinfo() - удобнейшее средство отладки. phpinfo(), помимо прочего, выводит значения всех $_GET, $_POST и $_SERVER - переменных. Так что, если переменная формы "потерялась", самый простой способ обнаружить, в чем дело - воспользоваться функцией phpinfo. Для того, чтобы функция выводила только значения переменных (и вам не пришлось прокручивать десяток страниц), ее следует вызвать следующим образом: phpinfo(INFO_VARIABLES);, или - что абсолютно то же самое - phpinfo(32);.

Пример:

<html>
<body>
<form method="post" action=/<span class="st0">"<?=$_SERVER['PHP_SELF']?>">
Введите Ваше имя: <input type="text" name="name">
<input type="submit" value="OK">
</form>
<?
phpinfo(32);
?>
</body>
</html>

Или, например, такая ситуация: вы хотите узнать IP-адрес посетителя. Вы помните, что соответствующая переменная хранится в массиве $_SERVER, но - вот незадача - забыли, как именно переменная называется. Опять же, вызываем phpinfo(32);, ищем в табличке свой IP-адрес и находим его - в строке _SERVER["REMOTE_ADDR"].

Ссылки
Реклама