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

Котеров Д. В., Костарев А. Ф. - PHP 5. 2-е издание (В подлиннике) - 2008

.pdf
Скачиваний:
6114
Добавлен:
29.02.2016
Размер:
11.36 Mб
Скачать

194

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

Листинг 10.4. Файл warn.php

<!-- Навязчивые предупреждения --> <form action=warn.php>

<input type=submit name="doGo" value="Click!"> </form>

<?

// В массиве $_REQUEST всегда содержатся пришедшие из формы данные. if ($_REQUEST['doGo']) echo "Вы нажали кнопку!";

?>

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

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

На самом деле текст предупреждения сохраняется в переменной PHP $php_errormsg, которая может быть в будущем проанализирована. Эта возможность доступна, если в настройках PHP включен параметр track_errors (по умолчанию он как раз и установлен в значение yes).

Вот теперь мы можем переписать наш пример, грамотно отключив надоедливое предупреждение (листинг 10.5).

Листинг 10.5. Файл warnoff.php

<!-- Отключение навязчивого предупреждения --> <form action="warnoff.php">

<input type=submit name="doGo" value="Click!"> </form>

<?php

// В массиве $_REQUEST всегда содержатся пришедшие из формы данные. if (@$_REQUEST['doGo']) echo "Вы нажали кнопку!";

?>

Как можно заметить, листинг 10.5 отличается от листинга 10.4 всего лишь наличием оператора @ внутри скобок инструкции if.

Глава 10. Выражения и операции PHP

195

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

Особенности оператора @

Оператор @ — это "быстрое решение", которое программист применяет, когда ему лень писать много кода. Часто его использование позволяет создавать более лаконичные и понятные программы, а это, как ничто другое, очень важно на начальном этапе разработки. Тем не менее в законченных скриптах рекомендуется по возможности избегать оператора @, и вот почему.

Ранее мы говорили, что PHP всегда выводит сообщения об ошибках в браузер. Это не совсем соответствует действительности: такое поведение задействуется лишь при включенной директиве display_errors, задающейся в файле php.ini. По умолчанию она включена.

Кроме того, интерпретатор имеет еще один настраиваемый режим работы, заставляющий его печатать ошибки не в браузер, а в файлы журнала сервера. Этот режим называется log_errors и задается одноименной директивой в файле php.ini. По умолчанию он отключен.

Вся проблема с оператором @ заключается в том, что он подавляет лишь вывод сообщений об ошибках в браузер, но не в журналы сервера. Таким образом, если вы рассчитываете на включение log_errors, использование оператора @ для вас нежелательно — иначе файлы журнала сервера быстро засорятся бессмысленными сообщениями.

Пример, приведенный в листинге 10.5, можно переписать и без использования оператора @. Делается это, например, так:

if (isset($_REQUEST['doGo'])) echo "Вы нажали кнопку!";

Конечно, писать каждый раз isset и пару скобок весьма утомительно. Кроме того, это ухудшает внешний вид кода. Зато так мы получаем программу, наиболее устойчивую к ошибкам.

Противопоказания к использованию

Действует правило: чем проще выражение, в котором вы отключаете предупреждения, тем лучше. А потому никогда не применяйте оператор @ в следующих случаях:

перед директивой include (включение другого файла с кодом);

перед вызовом собственных (не встроенных в PHP) функций;

перед функцией eval() (запуск строкового выражения как программы на PHP).

В идеальном случае вы должны применять @ только в одном случае — когда надо трактовать необъявленную переменную (или элемент массива) как "пустую" величину. В примерах выше мы так и поступали.

Данные рекомендации объясняются очень просто: заблокировав выдачу предупреждений в большом участке кода, вы рискуете получить большие сложности при отладке, если в этом коде произойдет какая-нибудь неожиданность.

196

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

Вот несколько примеров использования оператора @, которые не приводят к сколь- ко-нибудь серьезным проблемам при отладке:

//Проверка, установлен ли элемент массива. if (@$_REQUEST["doGo"]) echo "Кнопка нажата!";

//Разделение строки вида "ключ=значение" на пару переменных @list ($key, $value) = explode("=", $string);

//Открытие файла с последующей проверкой.

$f = @fopen("passwords.txt") or die("Не удалось открыть файл!");

Все затронутые в данном примере функции и конструкции мы обязательно рассмотрим в следующих главах.

Резюме

В данной главе мы научились оперировать основными элементами любой программы на PHP — выражениями. Узнали, что некоторые операции сильно изменяют свое поведение, если их выполняют не с обычными, а с логическими переменными, или с переменными, которые могут быть трактованы как логические. Мы выяснили, что понятия "равно" и "эквивалентно" для PHP сильно различаются, и PHP 4 "понимает" их немного не так, как PHP 5. Также был рассмотрен полезный оператор отключения предупреждений @ и ситуации, в которых его применение не рекомендовано.

ГЛАВА 1 1

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

Листинги данной главы можно найти в подкаталоге forms.

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

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

В гл. 1 мы довольно подробно рассмотрели механизм работы протокола HTTP, который отвечает за доставку данных из браузера на сервер и обратно. Впрочем, там было довольно много теории, так что предлагаем повторить этот процесс еще раз — так сказать, с прикладных позиций, а также разобрать возможности, предоставляемые PHP.

Передача данных командной строки

Вначале хотим вас поздравить: сейчас мы уже знаем достаточно, чтобы начать писать простейшие сценарии на PHP типа "Hello, world: сейчас 10 часов утра". Однако нашим сценариям будет недоставать одного — интерактивного взаимодействия с пользователем.

Поставим перед собой задачу написать сценарий, который принимает в параметрах две величины: зарегистрированное имя и пароль. Если зарегистрированное имя равно root, а пароль — Z10N0101, следует напечатать: "Доступ открыт для пользователя <имя>" и заблокировать сервер (т. е. вывести стандартный экран Windows "Блокировка" с запросом пароля для разблокирования). Если же данные неверны, необходимо вывести сообщение "Доступ закрыт!".

Конечно, это очень простой сценарий. Но и начинать лучше с простого.

198

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

Сначала рассмотрим наиболее простой способ передачи параметров сценарию — непосредственный набор их в URL после знака ? — например, в формате login=имя&password=пароль (мы рассматривали этот прием в части I). Правда, даже программисту довольно утомительно набирать эту строку вручную. Всякие там ?, &, %... К счастью, существуют удобные возможности языка HTML, которые, конечно, поддерживаются всеми браузерами.

Итак, пусть у нас на сервере в корневом каталоге есть сценарий на PHP под названием hello.php. Наш сценарий распознает 2 параметра: login и password. Таким образом, если задать в адресной строке браузера

http://example.com/hello.php?login=root&password=Z10N0101

мы должны получить требуемый результат.

Как только задача осознана, можно приступать к ее решению. Но прежде бывает полезно решить аналогичную, но более простую задачу. Итак, как же нам в сценарии получить строку параметров, переданную после знака вопроса в URL при обращении к сценарию? Как было указано в части I книги, для этого можно проанализировать переменную окружения QUERY_STRING, которая в PHP доступна под именем $_SERVER['QUERY_STRING']. Напишем небольшой пример, чтобы это проиллюстрировать (листинг 11.1).

Листинг 11.1. Файл qs.php

<!-- Вывод параметров командной строки. --> <html><body>

<?php

echo "Данные из командной строки: $_SERVER[QUERY_STRING]"; ?>

</body></html>

Если теперь мы запустим этот сценарий из браузера (перед этим сохранив его в файле test.php в корневом каталоге сервера) примерно вот таким образом:

http://example.com/qs.php?this+is+the+world

то получим документ следующего содержания:

Данные из командной строки: this+is+the+world

Обратите внимание на то, что URL-декодирование символов не произошло: строка $_SERVER['QUERY_STRING'], как и одноименная переменная окружения, всегда приходит в той же самой форме, в какой она была послана браузером. Давайте запомним этот небольшой пример — он еще послужит нам в будущем.

Так как PHP изначально создавался именно как язык для Web-программирования, то он дополнительно проводит некоторую работу с переменной QUERY_STRING перед передачей управления сценарию. А именно, он разбивает ее по пробельным символам (в нашем примере пробелов нет, их заменяют символы +, но эти символы PHP также понимает правильно) и помещает полученные кусочки в массив-список $argv, который впоследствии может быть проанализирован в программе. Заметьте, что здесь действует точно такая же техника, которая принята в C, с точностью до названия массива с аргументами.

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

199

Все же массив $argv используется при программировании на PHP крайне редко, что связано с гораздо бо´льшими возможностями интерпретатора по разбору данных, поступивших от пользователя. Однако в некоторых (обычно учебных) ситуациях его применение оправдано, так что не будем забывать об этой возможности.

Формы

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

Листинг 11.2. Файл form.html

<!-- Cтраница с формой --> <html><body>

<form action=hello.php>

Логин: <input type=text name="login" value=""><br> Пароль: <input type=password name="password" value=""><br>

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

</body></html>

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

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

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

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

200

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

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

Итак, мы не хотим заниматься прямым разбором переменной окружения QUERY_STRING, в которой хранятся параметры сценария. И правильно не хотим — интерпретатор перед запуском сценария делает все сам. Причем независимо от того, каким методом — GET или POST — воспользовался браузер. То есть, PHP сам определяет, какой метод был задействован (благо, информация об этом доступна через переменную окружения REQUEST_METHOD), и получает данные либо из QUERY_STRING, либо из стандартного входного потока. Это крайне удобно и достойно подражания, вообще говоря, в любых CGI-сценариях.

Все данные из полей формы PHP помещает в глобальный массив $_REQUEST. В нашем случае значение поля login после начала работы программы будет храниться в $_REQUEST['login'], а значение поля password — в $_REQUEST['password']. То есть, не надо ничего ниоткуда "получать" — все уже установлено и распаковано из URLкодировки. Максимум удобств, минимум затрат, не правда ли? К тому же, еще и работает быстрее, чем аналогичный кустарный код, написанный на PHP, потому что разработчики PHP предусмотрели функцию разбора командной строки на C.

Кроме того, чтобы можно было как-то разделить GET-параметры от POST-данных, PHP также создает массивы $_GET и $_POST, заполняя их соответствующими значениями. Нетрудно догадаться, что $_REQUEST представляет собой всего лишь объединение этих двух массивов.

Наш окончательный сценарий hello.php приведен в листинге 11.3.

Листинг 11.3. Файл hello.php

<!-- Использование данных формы -->

<html><body>

<?php

if ($_REQUEST['login']=="root" && $_REQUEST['password']=="Z10N0101") {

echo "Доступ открыт для пользователя " . $_REQUEST['login'];

// Команда блокирования рабочей станции (работает в NT-системах)

system("rundll32.exe user32.dll,LockWorkStation");

}else {

echo "Доступ закрыт!";

}

?>

</body></html>

Здесь мы применили инструкцию if (условное выполнение блока) и функцию system() (запуск команды операционной системы), которые нами еще не рассматривались. Надеемся, что читатель простит нам это — тем более, что скрипт весьма прост.

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

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

201

кнопки отправки. Если такая переменная существует, то, очевидно, что пользователь запустил программу, нажав кнопку (листинг 11.4).

Листинг 11.4. Файл lock.php

<!-- Усовершенствованный скрипт блокировки сервера --> <html><body>

<?if (!isset($_REQUEST['doGo'])) {?>

<form action="<?=$_SERVER['SCRIPT_NAME']?>"> Имя: <input type=text name="login" value=""><br>

Пароль: <input type=password name="password" value=""><br> <input type=submit name="doGo" value="Нажмите кнопку!"> </form>

<?} else {

if ($_REQUEST['login']=="root" && $_REQUEST['password']=="Z10N0101") { echo "Доступ открыт для пользователя $_REQUEST[login]";

// Команда блокирования рабочей станции (работает в NT-системах) system("rundll32.exe user32.dll,LockWorkStation");

}else {

echo "Доступ закрыт!";

}

}?>

</html></body>

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

Помните наши рассуждения о том, что же первично в PHP: текст или программа? Конструкция <?= применяется обычно в тот момент, когда выгодно считать, что первичен текст. В нашем примере именно так и происходит — ведь кода на PHP тут очень мало, в основном страница состоит из HTML-тегов.

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

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

202

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

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

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

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

Например, приведем сценарий (листинг 11.5), печатающий IP-адрес пользователя, который его запустил, а также тип его браузера (эти данные хранятся в переменных окружения REMOTE_ADDR и HTTP_USER_AGENT, доступных в скрипте через массив $_SERVER).

Листинг 11.5. Файл ip.php

<!-- Вывод IP-адреса и браузера пользователя --> <html><body>

Ваш IP-адрес: <?=$_SERVER['REMOTE_ADDR']?><br> Ваш браузер: <?=$_SERVER['HTTP_USER_AGENT']?> </body></html>

Трансляция cookies

Наконец, все cookies, пришедшие скрипту, попадают в массив $_COOKIES. Для иллюстрации рассмотрим скрипт, который считает, сколько раз его запустил текущий пользователь (листинг 11.6).

Листинг 11.6. Файл cookie.php

<?php ## Демонстрация работы с $_COOKIES.

//Вначале счетчик равен нулю. $count = 0;

//Если в cookies что-то есть, берем счетчик оттуда.

if (isset($_COOKIE['count'])) $count = $_COOKIE['count']; $count++;

//Записываем в cookies новое значение счетчика. setcookie("count", $count, 0x7FFFFFFF, "/");

//Выводим счетчик.

echo $count; ?>

Функцию setcookie() мы еще не рассматривали, но обязательно сделаем это в следующих главах. Она всего лишь посылает в браузер пользователя cookie с указанным именем и значением.

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

203

Обработка списков

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

<select name=Sel multiple>

<option>First

<option>Second

<option>Third

</select>

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

<select name="Sel[]" multiple>

<option>First

<option>Second

<option>Third

</select>

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

В результате мы получим в $_REQUEST массив массивов (или двумерный массив, как его еще называют), доступ к элементам которого можно получить так:

echo $_REQUEST['Sel'][0]; // выводит первый элемент echo $_REQUEST['Sel'][1]; // второй

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

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

$A[] = 10;

$A[] = 20;

$A[] = 30;

Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.

Оставленные комментарии видны всем.