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

web - tec / PHP 5 для начинающи

.pdf
Скачиваний:
121
Добавлен:
12.06.2015
Размер:
26.79 Mб
Скачать

Файлы и каталоги 323

Функция readdir() возвращает следующий пункт в списке открытого каталога. Этот список включает в себя такие пункты, как . (используется для указания текущего каталога) и .. (родительский по отношению к данному каталог). PHP+интерпретатор хранит внутренний указатель, связанный со следующим пунктом в списке. Этот указа+ тель подобен указателю позиции в файле, в которой должна быть выполнена следую+ щая файловая операция.

Практика Создание листинга каталога

Ниже показано, как создать цикл для получения всех пунктов в заданном каталоге:

<?php //dir_list.php

$default_dir ="x:/home/localhost/www/php5/Ch07";

if(!($dp = opendir($default_dir))) die("Невозможно открыть каталог $default_dir.");

while($file = readdir($dp))

if($file != '.' && $file != '..') echo "$file<br>"; closedir($dp);

?>

На рис. 7.8 показан пример выполнения данного сценария.

Рис. 7.8.

Как это работает

Сначала сценарий определяет дескриптор заданного каталога (в данном случае x:/home/localhost/www/php5/Ch07), а затем начинает цикл, который считывает записи из каталога и (если это не "." и не "..") распечатывает их. Цикл обусловлен

324 Глава 7

возвращаемым функцией readdir() значением, которое станет равным False, когда список записей в каталоге закончится:

while($file = readdir($dp))

if($file != '.' && $file != '..') echo "$file<br>";

Наконец, функция closedir() закрывает каталог.

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

<?php

$default_dir = "x:/home/localhost/www/php5/Ch07";

if(!($dp = opendir($default_dir))) die("Невозможно открыть каталог $default_dir.");

while($file = readdir($dp)) $filenames[] = $file; closedir($dp);

Теперь массив $filenames содержит все записи листинга каталога. PHP автома+ тически индексирует массив.

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

sort($filenames);

for($i=0; $i < count($filenames); $i++) if($filenames[$i] != '.' && $filenames[$i] != '..')

echo $filenames[$i] . "<br>";

?>

Другие функции для обработки каталогов

Как и в случае файлов, PHP обеспечивает множество способов для манипуляции с каталогами, включая следующие функции:

rewinddir()

chdir()

rmdir()

mkdir()

dirname()

(dir)

Функция rewinddir() переустанавливает внутренний указатель в PHP, когда не+ обходимо вернуться к первой записи в заданном каталоге. Эта функция является ана+ логом функции rewind() для файлов и требует указания соответствующего дескрип+ тора каталога. Остальные функции обработки каталогов вместо дескриптора непосредственно принимают строку, содержащую путь к каталогу.

Функция chdir() изменяет текущий каталог:

if(chdir("x:/home/localhost/www/php5/Ch07"))

echo "Текущий каталог: x:/home/localhost/www/php5/Ch07.";

else

echo "Невозможно перейти в каталог x:/home/localhost/www/php5/Ch07.";

Функция rmdir() удаляет заданный каталог. Каталог должен быть пустым, а кроме этого, сценарий должен иметь необходимые права для удаления данного каталога. Например:

rmdir("/tmp/rubbish/");

Файлы и каталоги 325

Функция mkdir() создает каталог, указанный в качестве ее первого аргумента. Можно также задать режим доступа к каталогу в виде трехзначного восьмеричного чис+ ла. Следующий код сначала проверяет, существует ли каталог x:/home/localhost/ www/php5/Ch07/test. Если это так, то сценарий удаляет этот каталог, а затем созда+ ет новый каталог с тем же именем и предоставляет всем пользователям все права дос+ тупа к данному каталогу (права доступа применимы только на Linux+системах):

$default_dir = "x:/home/localhost/www/php5/Ch07"; if(file_exists($default_dir)) rmdir($default_dir); mkdir($default_dir, 0777);

Функция dirname() возвращает путь к каталогу для заданного имени файла. На+ пример:

$filepath = "x:/home/localhost/www/php5/Ch07/index.html"; $dirname = dirname($filepath);

$filename = basename($filepath);

В результате в переменной $dirname теперь содержится строка "x:/home/ localhost/www/php5/Ch07", а в переменной $filename строка "index.html".

PHP предоставляет псевдо+объектно+ориентированный механизм для работы с ка+ талогами: класс dir. Чтобы использовать этот механизм, необходимо сначала создать объект путем вызова конструктора dir() и передачи ему имени каталога, с которым необходимо работать:

$dir = dir("x:/home/localhost/www/php5/Ch07");

Объект dir обладает двумя свойствами: handle и path. Они ссылаются на деск+ риптор каталога и путь соответственно:

echo $dir->handle; # распечатывает дескриптор каталога

echo $dir->path; # распечатывает строку "x:/home/localhost/www/php5/Ch07"

Свойство handle можно использовать с другими функциями обработки каталогов, такими как readdir(), rewinddir() и closedir().

Объект dir поддерживает три метода: read(), rewind() и close(), которые функ+ ционально эквивалентны PHP+функциям readdir(), rewinddir() и closedir() соответственно. Можно переписать сценарий создания листинга каталога, используя в нем описанный объект:

<?php

$default_dir = "x:/home/localhost/www/php5/Ch07"; $dir = dir($default_dir);

while($file = $dir->read()) if($file != '.' && $file != '..') echo $file . "<br>";

$dir->close(); ?>

Обход дерева каталогов

Как уже отмечалось в главе 6, рекурсия особенно полезна, когда сценарий должен выполнять повторяющиеся операции и обрабатывать в цикле определенное множе+ ство данных. Очень хорошим примером такого сценария является сценарий, выпол+ няющий обход дерева каталогов. В каталоге могут содержаться подкаталоги, а также файлы. Чтобы создать сценарий, который выводит имена всех файлов и подкаталогов указанного каталога, необходимо заставить сценарий считывать записи в текущем ка+ талоге. Если запись представляет собой имя файла, то ее следует отобразить. Если за+ пись является именем подкаталога, то необходимо отобразить его имя, а затем войти в него и снова вернуться к подпрограмме считывания записей в текущем каталоге.

326 Глава 7

Очевидно, что второй этап в случае необходимости повторяет весь процесс. Ре+ курсия продолжается до тех пор, пока не останется подкаталогов для обхода. Рас+ смотрим пример такого сценария:

<?php //nav_dir.php

$default_dir = "/home/james";

function traverse_dir($dir) {

echo "Обход каталога $dir....<br>";

chdir($dir);

if(!($dp = opendir($dir))) die("Невозможно открыть каталог $dir."); while($file = readdir($dp)) {

if(is_dir($file)) {

if($file != '.' && $file != '..') { echo "/$file<br>"; traverse_dir("$dir/$file"); chdir($dir);

}

}

else echo "$file<BR>";

}

closedir($dp);

}

traverse_dir($default_dir); ?>

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

function traverse_dir($dir) {

echo "Обход каталога $dir....<br>";

chdir($dir);

if(!($dp = opendir($dir))) die("Невозможно открыть каталог $dir."); while($file = readdir($dp)) {

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

if(is_dir($file)) {

if($file != '.' && $file != '..') { echo "/$file<br>";

Функция traverse_dir() вызывает сама себя, для того чтобы перемещаться вглубь по иерархии каталогов, и делает первоначальный каталог текущим:

traverse_dir("$dir/$file"); chdir($dir);

}

}

else echo "$file<BR>";

}

closedir($dp);

}

traverse_dir($default_dir); ?>

Мощь рекурсии, продемонстрированная в данном простом сценарии, позволяет создать собственную версию Linux+команды find.

Файлы и каталоги 327

Сценарий для навигации по каталогам

Теперь можно перейти к созданию довольно мощного сценария для навигации по каталогам (назовем его ‘‘навигатором’’), с помощью которого можно сканировать со+ держимое существующих каталогов и создавать новые. Для этого необходимо создать файл common_php5.inc.php с несколькими разделами, которые помогут приложе+ нию выполнять его работу.

Сначала следует установить каталог по умолчанию, имя файла и размер тексто+ вого поля:

<?php

//определение каталога по умолчанию $default_dir = "./docs";

//определение стандартного имени для новых файлов $default_filename = "new.txt";

//определение размера текстового поля $edit_form_cols = 80;

$edit_form_rows = 25;

Определение расширений файлов, которые можно обрабатывать:

//определение расширений файлов для обработки

$text_file_array = array( "txt", "htm", "html", "php", "inc", "dat" ); $image_file_array = array("gif", "jpeg", "jpg", "png");

Три описанные ниже функции создают заголовок, нижний колонтитул и необхо+ димые сообщения об ошибках:

function html_header() { ?>

<html>

<head><title>Добро пожаловать в текстовый редактор на Web-странице</title></head> <body>

<?php

}

function html_footer() { ?>

</body>

</html>

<?php

}

function error_message($msg) { html_header();

echo "<script>alert(\"$msg\"); history.go(-1)</script>"; html_footer();

exit;

}

Обработка информации о датах:

function date_str($timestamp) { $date_str = getdate($timestamp); $year = $date_str["year"];

$mon = $date_str["mon"]; $mday = $date_str["mday"]; $hours = $date_str["hours"];

$minutes = $date_str["minutes"]; $seconds = $date_str["seconds"];

return "$hours:$minutes:$seconds $mday/$mon/$year";

}

328 Глава 7

Следующая функция возвращает информацию о файле, а также проверяет расши+ рение файла:

function file_info($file) { global $text_file_array; $file_info_array["filesize"] =

number_format(filesize($file)) . " bytes."; $file_info_array["filectime"] = date_str(filectime($file));

$file_info_array["filemtime"] = date_str(filemtime($file)); if(!isset($_ENV[WINDIR])) {

$file_info_array["fileatime"] = date_str(fileatime($file)); $file_info_array["filegroup"] = filegroup($file); $file_info_array["fileowner"] = fileowner($file);

} else {

$file_info_array["fileatime"] = "недоступно"; $file_info_array["filegroup"] = "недоступно"; $file_info_array["fileowner"] = "недоступно";

}

$extension = array_pop(explode(".", $file));

if (in_array($extension, $text_file_array)) { $file_info_array["filetype"] = "текстовый";

} else {

$file_info_array["filetype"] = "бинарный";

}

return $file_info_array;

}

Сохраните весь код в файле common_php5.inc.php.

В начале сценария+навигатора необходимо подключить только что сохраненный файл:

<?php

//navigator.php

include "common_php5.inc.php";

Затем необходимо определить несколько функций. Во+первых, функцию mkdir_form(), которая отображает форму для создания нового каталога:

function mkdir_form() { global $dir;

?>

<center>

<form method="POST" action="<?php echo "$_SERVER[PHP_SELF]?action=make_dir&dir=$dir"; ?>"> <input type="hidden" name="action" value="make_dir"> <input type="hidden" name="dir" value="<? echo $dir ?>">

<?php

echo "<strong>$dir</strong>" ?>

<br>

<input type="text" name="new_dir" size="10">

<input type="submit" value="Создать каталог" name="Submit">

</form>

</center>

<?php

}

Функция make_dir() создает заданный каталог:

function make_dir() { global $dir;

if(!@mkdir("$dir/$_POST[new_dir]", 0700)) { error_message("Невозможно создать каталог $dir/$_POST[new_dir].");

Файлы и каталоги 329

}

html_header(); dir_page(); html_footer();

}

Затем функция display() распечатывает в новом окне содержимое заданного фай+ ла. Функция сравнивает расширение файла с элементами массивов $text_file_array и $image_file_array, определяя таким образом тип файла ++++++ текстовый, графиче+ ский или бинарный:

function display() {

global $text_file_array, $image_file_array; $extension = array_pop(explode(".", $_GET[filename]));

Затем расширение файла сравнивается с типами, которые определены в массивах $text_file_array и $image_file_array. Сценарий отказывается отображать двоичные файлы:

if(in_array($extension, $text_file_array)) { readfile("$_GET[dir]/$_GET[filename]");

}

else if(in_array($extension, $image_file_array)) {

echo "<img src=\"$_GET[dir]/$_GET[filename]\">";

}

else echo "Невозможно отобразить. Файл $_GET[dir]/$_GET[filename]

не является текстовым файлом или корректно сформированным графическим файлом.";

}

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

function dir_page() {

global $dir, $default_dir, $default_filename;

if($dir == ") {

$dir = $default_dir;

}

if (isset($_GET['dir'])) { $dir = $_GET['dir'];

}

$dp = opendir($dir);

?>

<table border="0" width="100%" cellspacing="0" cellpadding="0"> <?php

Создается два цикла для сортировки записей в заданном каталоге: цикл while для чтения всех записей в текущем рабочем каталоге и цикл for для отображения записей после сортировки:

while($file = readdir($dp)) $filenames[] = $file; sort($filenames);

for($i = 0; $i < count($filenames); $i++)

{

$file = $filenames[$i];

Если следующая запись в каталоге является точкой "." (т.е. указывает на текущий каталог), то функция игнорирует данную запись и начинает следующую итерацию

330 Глава 7

цикла. Кроме того, если текущий рабочий каталог является каталогом по умолчанию, то игнорируются как записи ".", так и записи "..":

if($dir == $default_dir && ($file == "." || $file == "..")) continue;

if(is_dir("$dir/$file") && $file == ".") continue;

if(is_dir("$dir/$file")) {

Если следующей является запись ".." (указывающая на родительский каталог), то функция отсекает имя текущего каталога от значения переменной $dir, чтобы соз+ дать гиперссылку на родительский каталог:

if($file == ".."){

В переменную $current_dir с помощью функции basename() записывается крайняя правая (после косой черты) подстрока имени каталога:

$current_dir = basename($dir);

Например, если переменная $dir содержит значение "/home/apache/htdocs/ images", то переменной $current_dir присваивается значение "images".

При создании гиперссылки функция dir_page() с помощью ereg_replace() удаляет вхождения этого значения в переменную $dir:

$parent_dir = ereg_replace("/$current_dir$","",$dir);

После этого переменная $parent_dir содержит значение "/home/apache/ htdocs", так как образец /$current_dir$ совпадает с подстрокой "/images" в конце строки.

echo "<tr><td width=\"100%\" nowrap>

<a href=\"$_SERVER[PHP_SELF]?dir=$parent_dir\">$file/ </a></td></tr>\n";

}

Если следующая запись является подкаталогом, то функция создает гиперссылку на этот подкаталог:

else echo "<tr><td width=\"100%\" nowrap>

<a href=\"$_SERVER[PHP_SELF]?dir=$dir/$file\"> $file/</a></td></tr>\n";

}

Если следующая запись является файлом, то функция создает гиперссылку для отображения содержимого данного файла в новом окне браузера:

else echo "<tr><td width=\"100%\" nowrap>

<a href=\"$_SERVER[PHP_SELF]?action=display&dir=$dir&filename=$file\" target=\"_blank\">$file</a></td></tr>\n";

}

?>

</table>

<?php mkdir_form();

}

Когда сценарий запускается, сначала он проверяет, не находится ли заданный ка+ талог выше каталога по умолчанию (по причинам безопасности пользователь не дол+ жен иметь доступа к файлам, которые находятся выше его каталога по умолчанию). Функция сравнения с образцом ereg() возвращает False, если переменная $dir не содержит значения $default_dir, и если это так, то переменной $dir присваивает+ ся значение $default_dir:

Файлы и каталоги 331

if(empty($dir) || !ereg($default_dir, $dir)) { $dir = $default_dir;

}

Наконец, вызываются функции, соответствующие значению переменной $action. По умолчанию вызывается функция dir_page():

if (!empty($_POST['action'])) { $action = $_POST['action'];

}

if (!empty($_GET['action'])) { $action = $_GET['action'];

}

switch ($action) { case "make_dir":

make_dir(); break;

case "display": display(); break;

default: html_header(); dir_page(); html_footer(); break;

}

?>

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

На рис. 7.10 показано содержимое файла new01.txt.

Рис. 7.9.

332 Глава 7

Рис. 7.10.

Создание текстового редактора

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

Во+первых, следует продумать, как в итоге будет выглядеть сценарий. Эта картина

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

вкотором сценарий сможет сохранять текст и давать пользователю возможность ра+ ботать с этим текстом. Нет необходимости беспокоиться о реализации таких специ+ фических функций редактирования, как ввод и удаление символов, перемещение кур+ сора и т.д., поскольку HTML+дескриптор <textarea> способен поддерживать все необходимые функции редактирования. Все, что требуется сделать, это создать фор+ му, на которой будет отображаться область ввода с полосой прокрутки, текстовое по+ ле для ввода имени файла и кнопка для сохранения файла.

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

<script> JavaScript-код </script>

Соседние файлы в папке web - tec