Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Последние темы форума

Показать новые сообщения »

Почтовая рассылка

Подписчиков: 11642
Последний выпуск: 19.06.2015

PHP и взаимодействие с файловой системой Web-сервера

Источник: www.ibm.com
Автор: Александр Деревянко
Дата: 3 августа 2010 года

Введение

В статье рассмотрены вопросы организации загрузки файлов на сервер с помощью PHP, работы с каталогами и файлами на сервере, запуск программ и использование переменных среды. Все это становится актуальным при необходимости решения задач, связанных с изменением Web-содержимого или предоставления дружественного интерфейса пользователю вместо традиционного ftp или scp.

Также мы рассмотрим создание HTML-формы для интерфейса, реализующего функционал загрузки файлов на сервер.

Код HTML-формы

<html>
<head>
  <title>Администрирование сервера - загрузка новых файлов</title>
</head>
<body>
<h1>Загрузка новых файлов с текстовым содержимым.</h1>
<form enctype="multipart/form-data" action="upload.php" method="post">
  <input type="hidden" name="MAX_FILE_SIZE" value="1000000">
  Загрузить файл: <input name="userfile" type="file">
  <input type="submit" value="Послать файл">
</form>
</body>
</html>

Форма содержит поля для ввода имени файла и дает возможность выбора такого файла из числа доступных на локальной машине.

Для посылки данных используется метод POST. Дескриптор <form> содержит атрибут enctype=”multipart/form-data” для указания серверу, что вместе с простой информацией будет посылаться и файл.

Форма содержит поле, указывающее на размер файла в байтах. Она имеет скрытый тип, изменяемый по желанию пользователя, – это размер файла (он должен быть согласован с сервером, у которого тоже есть подобные настройки – см. php.ini).

Поле ввода типа file обязательно, и задается следующим образом: <input name = “userfile” tipe=”file”>. Само имя может быть любым и должно совпадать с именем принимающего данные на стороне сервера сценария PHP.

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

Загружающий PHP-сценарий

Код сценария, принимающего данные формы, имеет вид:

<html>
<head>
  <title>Загрузка...</title>
</head>
<body>
<h1>Загрузка файла...</h1>
<?php

  if ($_FILES['userfile']['error'] > 0)
  {
    echo 'Проблема: ';
    switch ($_FILES['userfile']['error'])
    {
      case 1: echo 'размер файла больше upload_max_filesize'; break;
      case 2: echo 'размер файла больше max_file_size'; break;
      case 3: echo 'загружена только часть файла'; break;
      case 4: echo 'файл не загружен'; break;
    }
    exit;
  }
// Проверка, имеет ли файл правильный MIME-тип.

  if ($_FILES['userfile']['type'] != 'text/plain')
  {
    echo 'Проблема: файл не является текстовым';
    exit;
  }

// помещаем файл туда, куда нужно
  $upfile = '/uploads/'.$_FILES['userfile']['name'];

  if ($_FILES['userfile']['tmp_name'])
  {
    if (!move_uploaded_file($_FILES['userfile']['tmp_name'], $upfile))
    {
      echo 'Проблема: невозможно переместить файл в каталог назначения';
      exit;
    }
  }
  else
  {
    echo 'Проблема: возможна атака, использующая загрузку файла. Файл: ';
    echo $_FILES['userfile']['name'];
    exit;
  }

  echo  'Файл корректно загружен.<br /><br />';

// переформатирование содержимого файла
  $fp = fopen($upfile, 'r');
  $contents = fread($fp, filesize ($upfile));
  fclose ($fp);

  $contents = strip_tags($contents);
  $fp = fopen($upfile, 'w');
  fwrite($fp, $contents);
  fclose($fp);

// вывод загруженного файла
  echo 'Предварительный просмотр содержимого загруженного файла:<br /><hr />';
  echo $contents;
  echo '<br /><hr />';

?>
</body>
</html>

В приведенной программе имена функций и переменных зависят от версии PHP и настроек конфигурации, в частности, от включения настройки register_globals.

При загрузке файл помещается во временное хранилище для файлов Web-сервера. Если файл не переименовать и не переместить до окончания сценария, то он будет удален. Данные для обработки в сценарии хранятся в суперглобальном массиве $_FILES. Его элементы будут сохранены с именем дескриптора <file> из формы. Так как имя элемента формы имеет вид userfile, то содержимое массива $_FILES будет иметь следующие свойства:

  • значение в $_FILES[‘userfile’][‘tmp_name’] совпадает с хранилищем временных файлов на Web-сервере;
  • значение в $_FILES[‘userfile’][‘name’] представляет собой имя файла в локальной файловой системе пользователя формы;
  • значение в $_FILES[‘userfile’][‘size’] определяет размер файла в байтах;
  • значение в $_FILES[‘userfile’][‘type’] определяет MIME-тип файла – в нашем случае text/plane;
  • значение в $_FILES[‘userfile’][‘error’] определяет код ошибки, которая может возникнуть при загрузке файла.

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

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

Сначала проверяется код ошибки, возвращаемый $_FILES[‘userfile’][‘error’]. При этом каждому коду ошибки соответствует своя константа. Перечень возможных констант и соответствующих им кодов ошибок приведен ниже:

  • upload_error_ok = 0 – ошибок не было;
  • upload_err_ini_size = 1 – размер загружаемого файла превышает максимальное значение, заданное в php.ini директивой upload_max_filesize;
  • upload_err_form_size = 2 – размер загруженного файла превышает размер, заданный в HTML-форме;
  • upload_err_partial = 3 – загружена только часть файла;
  • upload_err_no_file = 4 – файл не загружен совсем.

Работа с каталогами

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

<html>
<head>
  <title>Просмотр каталогов</title>
</head>
<body>
<h1>Просмотр</h1>
<?php
  $current_dir = '/uploads/';
  $dir = opendir($current_dir);

  echo "<p>Каталог загрузки: $current_dir</p>";
  echo '<p>Содержимое каталога:</p><ul>';
  while ($file = readdir($dir))
  {
    echo "<li>$file</li>";
  }
  echo '</ul>';
  closedir($dir);
?>
</body>
</html>

Здесь применяются функции opendir(), closedir(), readdir(). Opendir() открывает каталог для чтения и возвращает дескриптор каталога так же, как и функция fopen(). После того как каталог открыт, можно прочитать имя файла с помощью специального вызова readdir($dir). Если каталог пустой или имя файла определено как «0», то функция возвратит false. Файлы при их выводе не сортируются, поэтому если нужен отсортированный список файлов каталога, то можно прочитать их имена в массив и отсортировать его перед выводом на экран. После завершения работы с каталогом вызывается функция closedir($dir) для его закрытия.

Возможно применение механизма ограничения доступных для просмотра файлов и каталогов, чтобы пользователь мог видеть только то, что ему позволено. Целесообразно, при необходимости, применение функции rewindirdir($dir), которая перемещает указатель чтения имен файлов в начало каталога.

Вместо перечисленных выше функций можно использовать класс dir, предоставляемый библиотекой классов php. В нем есть свойства handle и path, а также методы read(), close(), rewind(), выполняющие точно такие же действия, как и их простые аналоги.

Если имеется путь к файлу, то возможно получение дополнительных сведений об имени каталога и файла. Функции dirname($path) и basename($path) возвращают те части пути, которые соответственно содержат каталог и файл. Эту информацию можно применять для создания структуры каталогов, основанной на осмысленных именах файлов и каталогов.

Функция disk_free_space($path) может включить в список содержимого каталога индикацию свободного места для загружаемых файлов. При передаче этой функции пути к каталогу, она возвращает количество свободного места в байтах в файловых системах UNIX и Windows.

Работа с каталогами

Кроме простого просмотра каталогов, возможны операции по их созданию и удалению средствами PHP. Для этого применяют функции mkdir() и rmdir(). Заметим, что возможности таких манипуляций с каталогами определены только в тех случаях, когда доступ к ним разрешен пользователю, от лица которого выполняется сценарий.

Функция mkdir() принимает два параметра – путь к требуемому каталогу (включая имя создаваемого каталога) и права доступа, назначаемые для создаваемого каталога. Например, запись вида mkdir(“/tmp/testing”, 0777); – в данном примере задаваемые права на каталог совсем не обязательно будут результирующе такими же. В этом случае значение umask инвертируется и комбинируется с помощью операции AND, что и определит окончательное формирование параметров доступа. Например, если umask равна 022, то права доступа получатся в виде 0755. Для учета и предотвращения этого эффекта перед созданием каталога нужно сбросить текущее значение umask следующим образом:

	$oldmask = umask(0);
	mkdir(“/tmp/testing”, 0777);
	umask($oldmask);

Здесь задействована функция umask(), которая используется для получения нового значения прав доступа и для сброса имеющегося. Она заменяет текущее значение umask переданным ей параметром и возвращает старое значение при вызове ее без параметров. В системе Windows функция не производит никаких действий.

remove() предназначена для удаления каталога и применяется следующим образом:

rmdir(“/tmp/testing”);  или rmdir(“c:\\tmp\\testing”); 

В обоих случаях удаляемый каталог должен быть пустым.

Взаимодействие с файловой системой

Помимо функций просмотра и получения информации о каталогах и файлах на Web-сервере и записи информации в них возможно осуществление ряда других операций. Сценарий, реализующий получение дополнительной информации о файлах, имеет вид:

<html>
<head>
  <title>Информация о файле</title>
</head>
<body>
<?php
  $current_dir = '/uploads/';
  $file = basename($file);//удаление информации о каталоге для большей безопасности

  echo '<h1>Информация о файле: '.$file.'</h1>';
  $file = $current_dir.$file;
  echo '<h2>Данные о файле</h2>';
  echo 'Последнее обращение: '.date('j F Y H:i', fileatime($file)).'<br />';
  echo 'Последняя модификация: '.date('j F Y H:i', filemtime($file)).'<br />';

  $user = posix_getpwuid(fileowner($file));
  echo 'Владелец файла: '.$user['name'].'<br />';

  $group = posix_getgrid(filegroup($file));
  echo 'Группа файла: '.$group['name'].'<br />';

  echo 'Права доступа: '.decoct(fileperms($file)).'<br />';

  echo 'Тип файла: '.filetype($file).'<br />';

  echo 'Размер файла: '.filesize($file).' байтов<br />';

  echo '<h2>Статус файла</h2>';

  echo 'Каталог: '.(is_dir($file)? 'да' : 'нет').'<br />';
  echo 'Исполняемый: '.(is_executable($file)? 'да' : 'нет').'<br />';
  echo 'Файл: '.(is_file($file)? 'да' : 'нет').'<br />';
  echo 'Ссылка: '.(is_link($file)? 'да' : 'нет').'<br />';
  echo 'Разрешено чтение: '.(is_readable($file)? 'да' : 'нет').'<br />';
  echo 'Разрешена запись: '.(is_writable($file)? 'да' : 'нет').'<br />';
?>
</body>
</html>

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

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

  1. Функция exec() – ей в качестве аргумента передается командная строка, которую нужно выполнить. Например, exec(“ls –la”); эта функция не имеет непосредственных выходных данных и возвращает последнюю строку результата выполнения команды.
  2. passthru() – передает свои выходные данные в браузер. Используется при выводе изображения в различных форматах. Не возвращает никакого значения.
  3. system() – передает в браузер выходные данные команды. Пытается передать выходные данные каждой строки. Это отличает ее от passthru().
  4. Обратные кавычки – фактически это оператор выполнения команды. Результат выполнения возвращается в виде строки, которую затем можно отобразить в браузере или использовать другим путем.

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

Ниже приведен сценарий, содержащий эквивалентные примеры использования всех четырех способов:

<?php
   chdir('/uploads/');

///// версия exec
   echo '<pre>';
   // unix
   exec('ls -la', $result);
   // windows
   // exec('dir', $result);
   foreach ($result as $line) 
     echo "$line\n";
   echo '</pre>';
   echo '<br /><hr /><br />';

///// версия passthru
   echo '<pre>';
   // unix
   passthru('ls -la');
   // windows
   // passthru('dir');

   echo '</pre>';
   echo '<br /><hr /><br />';
 
///// версия system
   echo '<pre>';
   // unix
   $result = system('ls -la');
   // windows
   // $result = system('dir');
   echo '</pre>';
   echo '<br /><hr /><br />';

///// версия с обратными кавычками
   echo '<pre>';
   // unix
   $result = `ls -al`;
   // windows
   // $result = `dir`;
   echo $result;
   echo '</pre>';
?>

Один из этих способов может применяться вместо рассмотренного ранее сценария просмотра каталогов. Данный код не является переносимым и не может исполняться в среде Windows. При этом если в текст команды необходимо включать пользовательские данные, то их сначала нужно пропускать через функцию escapeshellcmd(). Это не даст возможности неавторизованным пользователям преднамеренно или случайно исполнять команды в системе Web-сервера. Можно в этих же целях воспользоваться функцией escapeshellarg() для отмены всех аргументов, которые нужно передать команде оболочки.

Взаимодействие с окружением

В арсенал средств PHP входит и возможность взаимодействия с переменными окружения. В этих целях используются функции getenv() и putenv(). Они позволяют соответственно получить и устанавливать эти переменные для дальнейшего использования. Речь идет о среде переменных Web-сервера, в которой выполняется PHP-сценарий.

Список всех переменных окружения можно получить, вызвав функцию phpinfo(). Например, getenv(“HTTP_REFERER”); возвратит URL-адрес страницы, с которой пользователь пришел на текущую.

Возможно устанавливать требуемые значения переменной среды с помощью putenv(), например:

$home = “/home/nobody”;
putenv (“ HOME = $home “);

При необходимости можно ограничить список переменных окружения, которые доступны для переустановки пользователями. Для этого можно воспользоваться директивой safe_mode_allowed_env_vars файла php.ini. При работе PHP в безопасном режиме пользователи могут менять только те переменные окружения, которые префиксно перечислены в этой директиве.

Выводы

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

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог