САМОУЧИТЕЛЬ PHP 4

Обработчики Apache


Итак, что же такое обработчик Apache? На самом деле мы постоянно сталкиваемся с одним из классических примеров обработчика. Да-да, вы уже догадались: это сам PHP. Если чуть углубиться в теорию, то обработчиком

называется сценарий (возможно, встроенный в сам сервер, как это происходит с PHP), который запускается сервером при попытке пользователя открыть ту или иную страницу определенного типа.

Каждый обработчик должен иметь уникальный идентификатор— имя обработчика, который я для краткости буду называть просто именем. Оно может состоять только из алфавитно-цифровых символов и знаков подчеркивания. Заметьте, что это имя — не то же самое, что имя файла сценария, в котором хранится код обработчика. Имя обработчика и является тем,  которое нужно указывать серверу в директиве AddHandler, когда мы хотим связать определенные документы с нашим сценарием.

Но как же сопоставить идентификатор обработчика тому сценарию, который содержит его код? У сервера Apache для этого есть специальная директива под названием Action. Где задается эта директива? В любом файле конфигурации Apache. Конечно, удобнее всего это делать в файле .htaccess, расположенном в корневом каталоге виртуального хоста, чтобы изменения распространились сразу на весь сервер. Мы уже рассматривали такую стратегию выше, только теперь все будет чуточку сложнее.

Вот требуемые директивы. Поместим их, как водится, в главный .htaccess-файл хоста.

# Сначала связываем имя обработчика с конкретным файлом.

# Знак "?" говорит серверу, что исходный URL запроса следует

# передать сценарию методом GET, т. е. через QUERY_STRING.

Action libhandler "/lib/libhandler.php?"

# Теперь уведомляем сервер, документы какого типа мы желаем

# "пропускать" через наш обработчик.

AddHandler libhandler .html .htm

В этом фрагменте есть два тонких места.



r    Путь к сценарию обработчика всегда

указывается как абсолютный URL без указания имени хоста и порта. Мы не можем задать здесь ни путь к файлу, ни даже относительный URL. По той причине, чтобы позволить одному обработчику "передавать эстафету" другому. В самом деле, ведь это и происходит в нашем примере: Apache сначала определяет, что документ нужно "пропустить" через обработчик libhandler, а т. к. он представляет собой ни что иное, как сценарий на PHP, то и через интерпретатор PHP. В деталях затронутый процесс чуть сложнее, но мы не будем в него углубляться.


r    После URL сценария в директиве Action следует знак ?. Зачем он? Это связано с механизмом, который использует Apache для того, чтобы определить конечный обработчик для того или иного документа. Когда пользователь посылает серверу URL, который, как Apache "знает", подходит под одну из команд Action, к этому URL слева просто присоединяется второй параметр директивы, и все начинается сначала — до тех пор, пока не будет найден последний обработчик. Например, если пользователь ввел /dir/file.html, то благодаря директиве Action указанный адрес преобразуется в /lib/libhandler.php?/dir/file.html. Это — ни что иное, как адрес PHP-сценария с параметром, который будет передан программе, как обычно, через переменную окружения QUERY_STRING.

Теперь сервер знает, что все документы с расширением html и htm нужно обрабатывать при помощи сценария, расположенного по адресу /lib/libhandler.php. Точнее, при каждой попытке открыть страницы с указанными расширениями Apache будет запускать наш сценарий и в числе обычных переменных окружения отправлять ему несколько специальных, содержащих первичную информацию о запросе, переданном пользователем. Мы сейчас рассмотрим эти переменные на практике. Если вас интересует их полный список, попробуйте распечатать массив $GLOBALS или воспользоваться функцией phpinfo(), вставив ее первой и единственной командой обработчика libhandler.php.



Вы, возможно, спросите, почему же мы не добавили в список расширений, на которые будет "реагировать" сценарий, еще одно — php? Давайте посмотрим, что бы произошло, поступи мы так. Предположим, пользователь хочет загрузить страницу /a.php. Apache "видит", что расширение у нее — php, и запускает обработчик с именем /lib/libhandler.php. Точнее, он "сваливает" всю работу на сценарий libhandler.php. Еще точнее — перенаправляет сервер по адресу /lib/libhandler.php?a.php! И тут же зацикливается, потому что у этого сценария расширение — также php. Итак, сервер начинает вызывать сценарий снова и снова, все удлиняя его URL — до тех пор, пока не "поймет": что-то неверно, и пора аварийно завершаться, о чем и сообщает в файлах журнала. О том, как решить эту проблему, рассказано в самом конце главы.



Ну вот, у нас уже почти все готово. Осталось только написать сам код обработчика. Это не так уж и сложно. Но прежде давайте вспомним, зачем мы вообще связались с обработчиками. Для автоматической загрузки библиотекаря перед выполнением того или иного сценария, помните? Что же, вот пример (листинг 29.5).



Мы подразумеваем, что обработчик libhandler.php находится в том же самом каталоге, что и библиотекарь с большинством модулей. Это довольно удобно, поскольку позволяет нам задавать путь к каталогу с модулями лишь в единственном месте — в директиве Action файла .htaccess, да и то в виде относительного URL. Оцените, насколько это проще для будущих модификаций сайта.

Листинг 29.5. Обработчик /lib/libhandler.php с подключением библиотекаря

<?

// Прежде всего, устанавливаем свои каталоги поиска модулей.

// Это, по нашей договоренности, — текущий в данный момент каталог.

$INC[]=getcwd();

// Ïðîâåðÿåì, íå ïûòàåòñÿ ëè ïîëüçîâàòåëü çàïóñòèòü îáðàáîò÷èê íàïðÿìóþ,

// ìèíóÿ Apache — íàïðèìåð, ïóòåì íàáîðà â áðàóçåðå àäðåñà

// /lib/libhandler.php. Òàê êàê àäðåñ, ââåäåííûé ïîëüçîâàòåëåì,

// âñåãäà ïåðåäàåòñÿ â ïåðåìåííîé îêðóæåíèÿ REQUEST_URI, òî íóæíî



// "áèòü òðåâîãó", åñëè ïåðåäàííàÿ ñòðîêà àäðåñà âñòðå÷àåòñÿ

// â èìåíè ôàéëà îáðàáîò÷èêà (ïðè÷åì â ëþáîì ðåãèñòðå ñèìâîëîâ).

// Ìû íå çàáûëè îòрåçàòü â ýòîé ñòðîêå ÷àñòü ïîñëå ?, ïîòîìó ÷òî

// îíà áóäåò ìåøàòü ïðè ñðàâíåíèè ñ èìåíåì ôàéëà.

// Ê ñîæàëåíèþ, ïîõîæå, ýòî åäèíñòâåííûé ïåðåíîñèìûé ìåæäó îïåðàöèîííûìè

// ñèñòåìàìè ñïîñîá ïðîâåðêè ëåãàëüíîñòè çàïóñêà îáðàáîò÷èêà.



$FileName=strtr(__FILE__,"\\","/");

$ReqName=ereg_Replace("\\?.*","",strtr(getenv("REQUEST_URI"),"\\","/"));

if(eregi(quotemeta($ReqName),$FileName)) {

  // Выводим сообщение об ошибке

  include "libhandler.err";

  // Записываем в журнал данные о пользователе

  $f=fopen("libhandler.log","a+");

  fputs($f,date("d.m.Y H:i.s")." $REMOTE_ADDR - Access denied\n");

  fclose($f);

  // Завершаем работу

  exit;

}

// Все в порядке — корректируем переменные окружения в соответствии

// с запрошенным пользователем адресом.

@putenv("REQUEST_URI=".

  $GLOBALS["HTTP_ENV_VARS"]["REQUEST_URI"]=

  $GLOBALS["REQUEST_URI"]=

  getenv("QUERY_STRING")

);

@putenv("QUERY_STRING=".

  $GLOBALS["HTTP_ENV_VARS"]["QUERY_STRING"]=

  $GLOBALS["QUERY_STRING"]=

  ereg_Replace("^[^?]*\\?","",getenv("QUERY_STRING"))

);

// Подключаем библиотекарь

include "librarian.phl";

   // Здесь можно выполнить еще какие-нибудь действия...

   // . . .

// Запускаем тот сценарий, который был запрошен пользователем

chdir(dirname($SCRIPT_FILENAME));

include $SCRIPT_FILENAME;

?>

Ну и, конечно, какая же программа обходится без вывода диагностических сообщений? Наш пример подгружает файл libhandler.err в случае "жульничества" пользователя. Наверное, в нем следует написать что-то типа:

<head><title>Доступ запрещен!</title></head>

<body>

<h2>Доступ запрещен!</h2>

Пользователь сделал попытку нелегально вызвать обработчик Apache,

отвечающий за автоматическое подключение библиотекаря. Так как это

свидетельствует о его желании нелегально проникнуть на сервер,

попытка была пресечена. Информация о пользователе записана

в файл журнала.

</body>

В результате

мы пришли к тому, что теперь все документы с расширениями html и htm рассматриваются как сценарии на PHP. Они запускаются уже после того, как подключен библиотекарь, так что могут пользоваться функцией Uses().



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


Содержание раздела