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

Самоучитель по PHP 4

.pdf
Скачиваний:
86
Добавлен:
02.05.2014
Размер:
4.36 Mб
Скачать

Глава 33. Разные советы

501

выполнить и загрузить этот сценарий с сервера. Звучит, как языческое заклинание, не правда ли? Пожалуй, с первого взгляда не совсем ясно, зачем же может понадобиться эта хваленая самопереадресация в Web-программировании.

Рассмотрим пример. Предположим, у нас имеется сценарий — гостевая книга наподобие той, эскиз которой мы рассматривали в главе 30. С точки зрения пользователя сценарий представляет собой страницу с адресом http://www.ourserver.ru/book/index.html. Если набрать этот адрес в браузе-

ре, появится, во-первых, форма с предложением добавить новое сообщение в книгу, а во-вторых, список ранее добавленных "посланий". В атрибуте action тэга <form> указан адрес той же самой страницы index.html (это вписывается в трехуровневую схему разработки сценариев), поэтому после набора сообщения и нажатия на кнопку отправки фактически снова загружается та же самая страница. Только перед ее загрузкой генератор данных гостевой книги определяет, что необходимо добавить новую запись, и делает это.

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

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

Однако ситуация становится еще плачевнее, если мы применяем в нашей гостевой книге метод GET. В этом случае при нажатии на кнопку Обновить браузер "без лишних разговоров" пошлет данные формы на сервер повторно, так что сообщение будет лишний раз добавлено в гостевую книгу без предупреждений. И это тоже понятно: ведь метод GET — не что иное, как простое изменение URL страницы, а именно, добавление в его конец символа ?, после которого следуют параметры (в том числе текст записи).

Впрочем, метод GET практически никогда не применяется в интерактивных сце- нариях, таких как гостевые книги, форумы и т. д. Мы уже говорили в первой части книги на эту тему, но она настолько важна, что я повторюсь. Если для

502

Часть V. Приемы программирования на PHP

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

жение вещей связано также и с тем, что некоторые proxy-серверы могут кэши- ровать страницы, полученные методом GET, но они никогда не кэшируют их при использовании POST.

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

Впрочем, при использовании самопереадресации очень легко наткнуться на один неприятный "подводный камень". Это — ошибка некоторых версий браузера Netscape, заключающаяся в том, что любые страницы, полученные им в результате самопереадресации, он ошибочно принимает за пустые (и соответственно отображает). И все же выход есть: достаточно немного модифицировать URL страницы, чтобы браузер "подумал", что это уже другой документ, а не тот же самый. Листинг 33.3 показывает, как это можно сделать. В целях экономии места я разместил шаблон страницы и генератор данных в одном файле.

Листинг 33.3. Самопереадресация

<?

// Считываем содержимое базы данных. $Book=@Unserialize(join("",File("book.dat"))); if(!$Book) $Book=array();

// Проверяем, не нужно ли добавить запись...

if(@$Go) { array_unshift($Book,$Text); $f=fopen("book.dat","w"); fwrite($f,Serialize($Book)); fclose($f);

//Внимание! Самопереадресация. Обратите внимание на то,

//какой заголовок мы посылаем.

Header("Location: http://$HTTP_HOST$REQUEST_URI?".time()); exit; // Завершить сценарий.

}

Глава 33. Разные советы

503

?>

<form action=sr.php method=post>

Введите текст:<br>

<input type=text name=Text><br>

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

<?foreach($Book as $k=>$v) {?> <?=$v?>

<hr>

<?}?>

Мы обеспечиваем "уникальность" URL страницы гостевой книги за счет добавления в его конец текущего времени в секундах, прошедших с 1 января 1970 года (так называемый Unix timestamp). Вряд ли пользователь будет обновлять страницу чаще, чем раз в секунду, поэтому такой способ прекрасно подходит для наших целей.

Обратите внимание на то, что в заголовке Location мы передаем полный URL страницы, включая имя хоста. Большинство браузеров умеют "понимать" и сокращенные пути (например, без указания имени сервера), но некоторые — нет, так что лучше не искушать судьбу.

Запрет кэширования страниц

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

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

В листинге 33.4 приведены четыре заголовка, которые необходимо послать вместе с телом страницы, чтобы браузеры и proxy-серверы не пытались ее кэшировать. Опыт подтверждает, что эти 4 заголовка — минимум. Если убрать хотя бы один из них, некоторые proxy-серверы (или браузеры) могут "не понять", что от них требуется.

Листинг 33.4. Заголовки для запрета кэширования

Header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

// Дата в прошлом

504 Часть V. Приемы программирования на PHP

Header("Last-Modified: ".gmdate("D, d M Y H:i:s")."GMT"); // Изменилась

Header("Cache-Control: no-cache, must-revalidate");

//

для

HTTP/1.1

Header("Pragma: no-cache");

//

для

HTTP/1.0

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

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

зультат работы сценария и шаблона буферизируется и не отправляется в браузер до самого последнего момента.

Несколько слов о флажках checkbox

Переключатель с независимым выбором (checkbox или более коротко — флажок) имеет одну довольно неприятную особенность, которая иногда может помешать Webпрограммисту. Вы, наверное, помните, что когда перед отправкой формы пользователь установил его в выбранное состояние, то сценарию в числе других параметров приходит пара имя_флажка=значение.

В то же время, если флажок не был установлен пользователем, указанная пара не посылается. Часто это бывает не совсем то, что нужно. Мы бы хотели, чтобы в невыбранном состоянии флажок также присылал данные, но только значение было равно какой-нибудь специальной величине — например, нулю или пустой строке.

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

Листинг 33.5. Гарантированная установка значений флажков

<? if(@$Go) {

foreach($Known as $k=>$v)

if($v) echo "Вы знаете язык $k!<br>"; else echo "Вы не знаете языка $k. <br>";

}

?>

<form action=lang.php method=post>

Какие языки программирования вы знаете?<br> <input type=hidden name=Known[PHP] value=0>

<input type=checkbox name= Known[PHP] value=1>PHP<br>

Глава 33. Разные советы

505

<input type=hidden name=Known[Perl] value=0>

<input type=checkbox name= Known[Perl] value=1>PHP<br> <input type=submit name=Go value="Go!">

</form>

Теперь в случае, если пользователь не выберет какой-нибудь из флажков, браузер отправит сценарию пару Known[язык]=0, сгенерированную соответствующим скрытым полем, и в массиве $Known создастся соответствующий элемент. Если пользователь выбрал флажок, эта пара также будет послана, но сразу же после нее последует пара Known[язык]=1, которая "перекроет" предыдущее значение.

Не включи мы скрытые поля в форму из листинга 33.5, сценарий печатал бы только сообщения о тех языках, которые "знает пользователь", пропуская языки, ему "неизвестные". В нашем же случае сценарий реагирует и на неустановленные флажки.

Такой способ немного увеличивает объем данных, передаваемых методом POST, за счет тех самых пар, которые генерируются скрытыми полями. Впро- чем, в реальной жизни это "увеличение" практически незаметно (особенно для

POST-форм).

ЧАСТЬ VI.

ПРИЛОЖЕНИЯ

Приложение 1

Файл конфигурации

Apache httpd.conf

Это приложение содержит полный текст файла конфигурации сервера Apache httpd.conf с комментариями на русском языке.

Содержимое листинга П1.1 полностью соответствует указаниям по настройке Apache, приведенным в части II книги. Если у вас по какой-то причине не получит- ся правильно установить Apache и PHP версии 4, руководствуясь этими указания- ми, представленный ниже текст файла httpd.conf решит все проблемы.

Несколько слов о формате httpd.conf. Файл состоит из строк, содержащих дирек- тивы Apache. В одной строке может быть расположено не более одной директивы. Текст от # äо конца строки считается комментарием и не берется в рассмотрение. Также игнорируются пустые строки.

При изменении начальной конфигурации файла возможно группирование нескольких директив в блоки, или контейнеры. При этом Apache поддерживает только ограниченное количество допустимых типов контейнеров. Любой блок-контейнер начинается строкой вида <ИмяКонтейнера>, расположенной, как обычно, на отдельной строке, и завершается тэгом </ИмяКонтейнера>. Некоторые (но не все) блоки могут быть вложенными.

Директивы, касающиеся индивидуальных настроек для каталогов или файлов, могут также помещаться в специальные файлы .htaccess, расположенные в соответствующих местах дерева каталогов сайта. Эти файлы должны иметь тот же формат, что и httpd.conf. Однако для них имеются особые ограничения на использование директив и блоков — список недопустимых можно найти в документации, поставляе-

мой с Apache.

Листинг П1.1. Файл конфигурации Apache httpd.conf

#Основан на конфигурационных файлах сервера NSCA, созданных

#Робом МакКулом.

#

# Главный файл конфигурации сервера Apache, содержащий директивы,

510

Часть VI. Приложения

#управляющие работой сервера. За более детальной информацией

#обращайтесь по адресу http://www.apache.org/docs/.

#

#Не стоит читать эти директивы без понимания их роли. Они

#приведены здесь лишь в качестве примера одного из возможных

#вариантов. В случае сомнений обращайтесь к сопроводительной

#документации. Считайте, что вас предупредили.

#

#После просмотра и анализа файла httpd.conf сервер

#попробует найти и обработать файлы:

#C:/Program Files/Apache Group/Apache/conf/srm.conf, а затем

#C:/Program Files/Apache Group/Apache/conf/access.conf,

#если вы не переопределили эти имена директивами ResourceConfig

#и/или AccessConfig.

#

#Директивы конфигурации сгруппированы в три основных раздела:

#1. Директивы, управляющие процессом Apache в целом (глобальное

#окружение).

#2. Директивы, определяющие параметры "главного" сервера, или

#сервера "по умолчанию", отвечающего на запросы, которые

#не обрабатываются виртуальными хостами. Эти директивы задают

#также установки по умолчанию для всех остальных виртуальных хостов.

#3. Установки для виртуальных хостов, позволяющие обрабатывать

#запросы Web одним-единственным сервером Apache, но направлять

#по раздельным IP-адресам или именам хостов.

#

#Файлы конфигурации программы и журналы регистрации событий

#(в программисткой среде они чаще называются "конфигами" и "логами",

#так что, я думаю, ничего страшного не произойдет, если я буду

#придерживаться этой терминологии и здесь).

#Если имена файлов, определенных вами для управления сервером,

#начинаются с символа / (или "диск:/" для Win32), сервер будет

#использовать явно указанный в этом имени полный путь. Если же имена не

#начинаются с "/", то для определения пути будет задействовано значение

#директивы ServerRoot. Так, logs/foo.log при значении ServerRoot,

#равном /usr/local/apache, будет интерпретироваться сервером как

#/usr/local/apache/logs/foo.log.

#