PHP-4 Эффективная работа


Управление передачей HTTP-заголовков
Установка «плюшек»: setcookie
Взаимодействие с сервером www
Загрузка файлов на сервер
Работа с удаленными файлами
Управление соединениями
Аутентификация пользователей

15.3. Взаимодействие с сервером WWW

В этом разделе мы рассмотрим, каким образом PHP-машина может взаимодействовать со внутренними механизмами web-сервера Apache.

15.3.1. Псевдозапрос: apache_lookup_uri

class apache_lookup_uri (string filename)

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

statusthe_request status_line
methodcontent_type handler
urifilename path_info
argsboundary no_cache
no_local_copyallowed send_bodyct
bytes_sent byterange clength
unparsed_urimtime request_time

15.3.2. Работа с таблицами запросов:apache_note

string apache_note (string note_name [, string note_value])

Эта функция, специфичная для Apache, позволяет считывать и устанавливать значения в таблице notes, используемой при обслуживании запросов пользователей. Функция вызывается с одним единственным аргументом и возвращает текущее значение заметки note_name. При использовании второго, необязательного аргумента указанной заметке будет присвоено новое значение, а результатом функции будет предыдущее значение note_name.

15.3.3. Преобразование строк: ascii2ebcdic

int ascii2ebcdic (string ascii_str)

Функция ascii2ebcdic() представляет собой функцию, специфичную для Apache, которая используется (и доступна) только при работе в операционных системах, использующих эту кодировку — OS/390, BS2000. Функция осуществляет преобразование строки ascii_str в кодировке ASCII в ее эквивалент в кодировке EBCDIC, который возвращается как результат работы функции.

15.3.4. Преобразование строк: ebcdic2ascii

int ebcdic2ascii (string ebcdic_str)

Функция ebcdic2ascii() представляет собой функцию, специфичную для Apache, которая используется (и доступна) только при работе в операционных системах, использующих эту кодировку, — OS/390, BS2000. Функция осуществляет преобразование строки ebcdic_str в кодировке EBCDIC в ее эквивалент в кодировке ASCII, который возвращается как результат работы функции.

15.3.5. Загрузка всех заголовков: getallheaders

array getallheaders ()

Функция возвращает ассоциативный массив, содержащий все HTTP-заголовки, использованные при запросе текущей страницы. Это позволяет, в частности, считывать значения общих CGI-переменных.

Использовать эту функцию можно, например, вот так:
$headers = getallheaders( );
while (list ($header, $value) = each ($headers)) {
       echo "$header: $value<br>\n";

15.3.6. Субзапрос: virtual

int virtual (string filename) Функция vi rtual () представляет собой функцию, специфичную для Apache, которая эквивалентна конструкции
<!--#include virtual...-->

Ее назначение состоит в выполнении субзапроса Apache. Применяется эта функция в основном для включения CGI-программ, shtml-файлов и прочих конструкций, разбираемых на стороне сервера. Имейте в виду, что в случае с CGI програм- ма должна генерировать корректный набор заголовков CGI. Как минимум вы должны самостоятельно сгенерировать заголовок «Content-type». При работе с PHP-файлами вы должны использовать include() (см. раздел 6.12) или requirе() (см. раздел 6.11), поскольку virtual () не может использоваться для включения документа, представляющего собой РНР-программу.

15.4. Загрузка файлов на сервер

Было бы странно, если бы PHP-машина не поддерживала механизм загрузки файлов от удаленного компьютера клиента. Предусмотренные для этого средства позволяют загрузить пользователю как текстовые, так и двоичные данные, а восполь- зовавшись механизмом аутентификации и функциями работы с файловой системой (см. главу 10), вы можете управлять процессом загрузки и по своему усмотрению распоряжаться загруженной информацией.

15.4.1. Загрузка файлов методом POST

Как правило, для загрузки файлов используется метод POST, поскольку он органично вписывается в процесс ввода и обработки переменных формы, но при желании вы можете воспользоваться и методом PUT (см. раздел 15.4.4). Но вначале мы все-таки рассмотрим наиболее простой и распространенный подход.

Для загрузки файла нам необходимо создать специальную форму, которая выглядит примерно так:
<FORM ENCTYPE="multipart/form-data" ACTION="_URL_" METHOD=P05T>
<INPUT TYPE="hidden" name="MAX_FILE_SIZE" value="1000">
Отправить файл: <INPUT NAME="userflie" TYPE="fUe">
<INPUT TYPE="submit" VАLUЕ="Отправка">
</FORM>

Понятно, что _URL_ должен указывать на РНР-нрограму, которая и займется обработкой формы. Скрытое поле MAX_F ILE_S IZE должно предшествовать полю ввода содержимого файла. Оно определяет максимальный разрешенный размер файла. Как правило, это значение задается в байтах.

В программе, обрабатывающей эту форму, в ваше распоряжение поступает глобальный массив $HTTP_POST_FILES, в котором вы найдете всю необходимую информацию. При этом для каждого поля формы ввода файла создается массив, который содержит несколько элементов. Для приведенного в рассматриваемой форме файла userfile мы получим следующее:

  • $HTTP_P05T_FILES [' userf i le ' ] [' name ' ] — первоначальное имя файла на клиентской машине;
  • $HTTP_POST_FILES[ 'userf ile' ] [' type' ] — MIME-тип загружаемого файла (конечно, если программа-навигатор содержит эту информацию); это может быть, например, «image/gif»;
  • $HTTP_POST_FILES [' userf ile '] ['size'] — размер загружаемого файла в байтах;
  • $HTTP_POST_FILES ['userfile'] [' tmp_name ' ] — имя временного файла, в который помещается содержимое загруженного на сервер файла.

Загружаемые из внешнего мира файлы автоматически помещаются в каталог временного хранения на сервере, если иное не указано директивой upload_tmp_dir в конфигурационном файле php.ini. Что касается временного каталога WWW-сервера, то вы можете изменить его, установив собственное значение переменной окружения TMPDIR для окружения, внутри которого запускается РНР-машина.

примечание
       Бесполезно устанавливать значение этой переменной с помощью функции putenv() внутри PHP-программы — это не сработает!

Теперь позвольте предложить вашему вниманию два примера обработки представленной выше формы. Вот «современный вариант», который будет работать как в РНР 3, так и в РНР 4 начиная с 4.0.3:

<?php if (is_uploaded_file($userfile) ) <
       copy ($userfile, "/place/to/put/uploaded/file") ;
} else {
       echo "Возможно, атака класса file upload: ' $userfile ' . " ;
}
/* . . .or. . . »/
move_uploaded_f i le($userfile, "/place/to/put/uploaded/file") ;
?>

Если же ваша версия РНР застряла между 4.0.0 и 4.0.2, то решение должно быть иным. Вот что вам придется использовать:

<?php
/* Проверка параметров загружаемого файла */
function is_uploaded_file($f ilename) {
       if ( !$tmp_file = get_cfg_var (' upload_tmp_dir ')) {
         $tmp_file = dirname(tempnam( ' ' , ''));
       }
       $tmp_file .= '/' . basename($f ilename) :
       /* В php.ini могли остаться косые... */
      return (ereg_replace( ' /+' , '/', $tmp_file) == $filename);
}
if (is_uploaded_file($userfile) ) {
       copy ($userf ile, "/place/to/put/uploaded/file") ;
} else {
       echo "Возможно, атака класса file upload: ' $userfile ' . " ;
       }
       ?>

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

Если вы работаете с PostgreSQL, то вам может оказаться удобным переносить файл из временной области в BLOB-поле одной элементарной операцией.

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

15.4.2. ЧАВГИ при загрузке файлов

ЧАВГИ — это «ЧАсто Встречающиеся ГраблИ», на которые наступает большое количество разработчиков. В этом маленьком разделе мы коснемся тех моментов, которые осложняют жизнь при загрузке файлов.

Ранее мы ввели в форме скрытое поле MAX_FILE_SIZE, которое автоматически обрабатывается РНР при обработке формы еще до передачи результатов (и управления) программе пользователя. Его значение не может превышать то, которое было установлено в соответствующей конфигурационной директиве php.ini или httpd.conf. По умолчанию это значение равно 2 Мбайт.

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

15.4.3. Загрузка нескольких файлов

Как вы наверняка уже догадались из структуры переменной HTTP__POST_FILЕS, нет никаких препятствий к тому, чтобы загружать на сервер сразу несколько файлов. Для этого вам всего лишь потребуется использовать в HTML-форме синтаксис массивов РНР.

Вот как может выглядеть такая форма:
<form action="file-upload.php" method="post"
          enctype="multipart/form-data">
       Отправить на сервер файлы:<br>
       <input name="userfile[]" type="f ile"><br>
       <input name="userfi le []" type="file"><br>
       <input type="submit" vаluе="0тправка">
</form>

При отправке на сервер этой формы массивы $userfile, $userf11e_name и $userfiIe_size получают глобальную область видимости в программе file-upload.php, также как и переменная $HTTP_POST_FILES. Каждый из них представ- ляет собой массив с числовой индексацией.

Предположим, например, что мы передаем на сервер файлы с именами /home/test/ review.html и/home/test/xwp.out. В этом случае переменная $userf ile_name [0] будет содержать значение review, html, a $userf ile_name[l] — значение xwp. out. Аналогичным образом в $userfile_size [0] будет помещен размер файла review.html и т. д.

15.4.4. Поддержка метода PUT

PHP-машина поддерживает также метод загрузки PUT, предназначенный специально для передачи документов на сервер и используемый такими программами, как Netscape Composer или W3C Amaya. Запросы PUT в силу их специализации гораздо более простые, чем загрузка файла пользователем, и реализуются с помощью HTTP-заголовков вида:

PUT /path/filenae.html HTTP/1.1

Как правило, это означает, что удаленный клиент пытается сохранить содержание передаваемого документа в дереве вашего WWW-сервера как /path/filename.html. Конечно, в большинстве случаев не стоит давать возможность удаленным пользователям непосредственно оперировать с деревом документов. Так что вы должны указать вашему серверу, что подобные запросы следует предварительно обраба- тывать подготовленной вами PHP-программой. В частности, при работе с сервером Apache для этого используется директива Script, которая обычно помещается внутри блока <Di rectory> или <Virtualhost>.

Все, что вам необходимо, — это следующая строка:
Script PUT /put-filter.php

В результате все запросы PUT, получаемые Apache, будут пропускаться через указанный вами фильтр. А сам фильтр может представлять собой что-нибудь вроде:

<?php copy($PHP_UPLOADED_FILE_NAME,
                  $DOCUMENT_ROOT.$REQUEST_URI); ?>

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

Запрос PUT, как и POST, при обработке PHP-машиной приводит к формированию временного файла, который будет уничтожен при завершении работы программы (в нашем случае — программы-фильтра), поэтому вы должны самостоятельно позаботиться о копировании файла в нужное место. Имя этого временного файла помещается в переменную $PHP_PUT_F ILENAME, а предлагаемое пользователем место в дереве документов — в переменную $REQUEST_URI. Конечно, при этом пользователю часто попросту неизвестен путь к корню дерева документов на сервере.

назад
далее

Сайт управляется системой uCoz