Шаг 2 - Получение данных от пользователя в PHP

Я уже говорил об опасности интернета. Тут обитает все что угодно, неграмотные пользователи, профессиональные хакеры, различные автоматические роботы делающие индексирование данных или просто собирающих почтовые адреса для спаммерских баз рассылки. Вобщем, контингент специфический, и со всеми нужно уметь работать. А правильная работа программы - это залог здоровья программиста, в прямом и переносном смысле :)) Поэтому давайте поговорим о том, как получить данные от пользователей.

Чтобы сразу внести ясность и разработать правильный подход к программированию давайте разделим условно всю программу на несколько частей:

  1. Блок инициализации переменных программы
  2. Блок получения данных от пользователя
  3. Блок проверки полученных данных от пользователя
  4. Блок основной программы
  5. Блок вывода обработанных данных

Конечно же данное разделение условно и можно некоторые блоки перемещать или объединять. Но вы должны усвоить главное - никогда не верь полученным данным и проверяй все. Поэтому перед началом работы нужно всегда установить или получить данные и проверить их на адекватность.

Для начала Вы должны знать, что такое формы в HTML, ведь именно для обработки форм, в большинстве случаев используется PHP, если Вы еще это не знаете, то тогда читайте "Шаг 19 - Изящные формы". Если с этим проблем пока нет, то давайте пойдем дальше...

Перечада значений параметров формы происходит посредством методов GET и POST. Дополнительным источником данных от пользователя могут быть cookies, так называемые "куки" (но об этом позже). Методом GET данные передаются непосредственно через URL запрашиваемой страницы после знака "?". Например:

index.php?param1=goto¶m2=http://www.firststeps.ru/php/

Размеры данных, которые можно передать таким способом ограничены 32 килобайтами, поэтому для передачи более массивных данных используется метод POST, который встраивает данные в тело HTTP запроса к серверу.

В качестве примера создадим следующую форму для передачи данных:

<form action=form.php method=post>
Первый параметр: <input type=text name=param1>
Второй параметр: <input type=text name=param2>
</form>

Вся прелесть PHP в том, что Вам не надо заботиться о получении, раскодировании и любой другой обработке данных из формы, как например при написании CGI программы на языке C/C++. За нас все делает PHP. Очень легко и красиво он автоматически заполняет несколько встроенных массивов:

Массивы $HTTP_*_VARS указанные в скобках использовались до версии PHP 4.1.0, поэтому их нужно использовать, если Вы разрабатываете скрипты заведомо под старые версии интерпретатора. Все остальные являются современными и предпочтительными, кроме того значения этих массивов являются "суперглобальными", т.е. видимыми везде в коде, в то время как раньше для работы с $HTTP_*_VARS внутри функций приходилось делать объявление этих переменных с помощью global.

Таким образом практически всю самую сложную работу в процессе получения данных PHP делает автоматически. Например, для печати данных нашей формы можно написать следующий код:

<?php
echo "Параметр 1 = ",$_POST["param1"];
echo "Параметр 2 = ",$_POST["param2"];
?>

Так как мы передавали значения посредством метода POST, то значения будут храниться в соответствующих ячейках массива $_POST[...]. Но тут следует сделать одно очень полезное замечание. У нас есть массив $_REQUEST, который в себе объединяет три массива, поэтому для создания программ в стиле "мне все по барабану", т.е. в не зависимости от метода передачи данных, я бы Вам порекомендовал использовать именно его. Конечно бывают случаи, когда требуется разделить методы приема, но тогда Вы сами решите для себя какой массив использовать $_GET[...] или $_POST[...].

Теперь теоритически Вы должны знать откуда брать данные, которые передает форма, по крайней мере с помощью GET и POST. А сейчас хотелось бы поговорить об стремлении разработчиков PHP еще более упростить метод получения данных. В погоне за доступностью они довели этот процесс до абсурда, и потом почти вся общественность признала практически все эти методы опасными с точки зрения безопасности. Я говорю о таких настройках, как register_globals, magic_quotes_gpc и magic_quotes_runtime. Все данные настройки с одной стороны упрощают жизнь разработчику, а с другой делают его невнимательным и не контролирующим процесс работы с данными.

Например, register_globals создает переменные имеющие имя переданного параметра, т.е. например параметр param1, который передает наша форма будет доступен через $_POST["param1"], $_REQUEST["param1"] и через созданную этой настройкой переменную $param1. Этот последний вариант и был признан опасным. Например, если вы создали такую связку файлов:

===== index.php =====

<?php
include("login.php");

if ($login_ok == 1) {
	echo "Программа работает";
} else {
	echo "Чего вам надо ?!";
};
?>


===== login.php =====

<?php
if (($_GET["login"] == "admin") && 
	($_GET["passwd"] == "1234"))
{
	$login_ok = 1;
};
?>

Тут, якобы, происходит проверка логина и пароля, которые передаются через форму. В файле login.php есть вроде и не сильно заметная ошибка, зато она может очень дорого стоить. Тут просто не делается присвоение $login_ok = 0 в случае, если логин и пароль не совпали. Если постоянно думать о безопасности, то конечно такую ошибку допустить сложно, но новички постоянно это делают, наступая на все теже грабли человечества. Так вот, если параметр register_globals для удобства установлен в On, то запрос к серверу index.php?login_ok=1 приведет к плачевным результатам.

Бороться с этим явлением конечно можно попробовать таким образом:

ini_set("register_globals","0");

Но к сожалению такой фокус не пройдет. Так как переменные создаются еще до запуска самой программы и отключение этого режима ничего не сделает. Но в PHP есть две, в данном случае незаменимые функции unset() и eval(). Функция unset() удаляет из памяти указанный объект, а eval() позволяет рекурсивно вызвать динамически созданный код. Ведь все дело в том, что изначально мы незнаем количество и названия переданных параметров, поэтому просто unset() мы не сможем вызвать, его надо будет создавать динамически. И вот код:

<?php
foreach ($_REQUEST as $name => $value) {
	eval("unset(\$".$name.");");
};
?>

В данном случае будут удалены все переменные, которые создались с помощью register_globals и больше их в памяти не будет. Думаю для этого хватит одного лишь массива $_REQUEST.

Дополнение от 09-02-2006: Получил письмо от Антонинко Сергея <pr***@umistudio.com>, который заставил обратить внимание на такое понятие в PHP как Variable variables, т.е. чтобы не было "масло маслянное" можно это перевести как "переменные с изменяемыми именами". Сначала, как "Фома", я в это не поверил, но потом отыскал описание в документации и убедился в том, что его код должен работать:

foreach($_REQUEST as $key => $val) {
	if(isset($$key)) unset($$key);
};

Достаточно элегантное решение, которое в очередной раз доказывает "глубину" и "мощность" языка PHP. Теперь будем знать, что существует магический значок $$. Еще остается вопрос быстродействия, какое из решений быстрее работает, но скорее всего второе.

Я до конца еще не знаю, стоит ли все-таки бороться такими жестокими методами с register_globals, может просто стоит для любой переменной, которую Вы собираетесь использовать, сначала произвести инициализацию, которая убережет Вас от неприятностей. Именно поэтому я поставил блок инициализации переменных программы на первое место, потому что, если сделать так:

<?php
$login_ok=0;

include("login.php");

if ($login_ok == 1) {
	echo "Программа работает";
} else {
	echo "Чего вам надо ?!";
};
?>

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

К очередному "облегчению" жизни стоит отнести "полезную" функцию магических кавычек magic_qoutes_gpc, когда все кавычки в данных отбиваются ESC-последовательностями:

Было: Старческий "маразм" крепчает "с годами".
Стало: Старческий \"маразм\" крепчает \"с годами\".

Это полезно в случае, если Вы работаете с базой данных, в которой все передающиеся данные помещаются внутрь кавычек. Но совершенно бесполезно, если Вы просто хотите работать с полученными данными. Получается, что у Вас в строке как "по щучьему велению" появилась куча символов "\", которые непонятно для чего и откуда взялись и мешают правильно работать вашему навороченному алгоритму. Нехорошо... А лекарство такое:

if (get_magic_quotes_gpc()) {
	$_GET = array_map('stripslashes', $_GET);
	$_POST = array_map('stripslashes', $_POST);
	$_COOKIE = array_map('stripslashes', $_COOKIE);
};
ini_set("magic_quotes_gpc","0");
ini_set("magic_quotes_runtime","0");

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

<?php
/*foreach ($_REQUEST as $name => $value) {
	eval("unset(\$".$name.");");
};*/
foreach($_REQUEST as $key => $val) {
	if(isset($$key)) unset($$key);
};
if (get_magic_quotes_gpc()) {
	$_GET = array_map('stripslashes', $_GET);
	$_POST = array_map('stripslashes', $_POST);
	$_COOKIE = array_map('stripslashes', $_COOKIE);
};
ini_set("magic_quotes_gpc","0");
ini_set("magic_quotes_runtime","0");
ini_set("display_errors","1");
if (version_compare(phpversion(), "5.0.0", ">")==1) {
	ini_set("error_reporting", E_ALL | E_STRICT);
} else {
	ini_set("error_reporting", E_ALL);
};
?>

Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Кузин Андрей.