Скачиваний:
20
Добавлен:
02.05.2014
Размер:
906.24 Кб
Скачать

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

С точки зрения операционной системы каталоги – это те же самые файлы, только со специальным именем. То есть директорию можно представить себе как файл, в котором хранятся имена и местоположения других файлов и каталогов. Этим обеспечивается традиционная древовидность организации файловой системы в различных ОС. С каждым процессом (в частности, и с работающим сценарием) ассоциирован свой так называемый текущий каталог. Все действия по работе с файлами и каталогами осуществляются по умолчанию именно в ней. Например, если мы открываем файл, указав только его имя, PHP будет искать этот файл именно в текущем каталоге. Существуют также и функции, которые могут сделать текущим любой указанный каталог.

Манипулирование каталогами

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

bool mkdir(string $name, int $perms)

Создает каталог с именем $name и правами доступа $perms. Права доступа для каталогов указываются точно так же, как и для файлов. Чаще всего значение $perms устанавливают равным 0770 (предваряющий ноль обязателен – он указывает PHP на то, что это – восьмеричная константа, а не десятичное число). Например:

mkdir("my_directory",0755); // создает подкаталог в текущем каталоге

mkdir("/data"); // создает подкаталог data в корневом каталоге

В случае успеха функция возвращает true, иначе – false. Необходимо заметить, что пользователь не может создать подкаталог в родительском каталоге, права на запись в который у него отсутствуют. Здесь точно такая же ситуация, как и с файлами. Вы, наверное, заметили, что атрибуты доступа 0770 означают "доступен для чтения, записи и исполнения для владельца и его группы". Что означает атрибут исполнения, установленный для каталога? Может быть, он разрешает пользователям запускать из него программы? А вот и нет. Право на "исполнение" показывает, что пользователь сможет просмотреть содержимое каталога. Конечно, все это специфично для операционных систем семейства Unix.

bool rmdir(string $name)

Удаляет каталог с именем $name. В случае успеха возвращает true, иначе – false. Как всегда, действуют стандартные ограничения файловой системына эту операцию.

bool chdir(string $path)

Сменяет текущий каталог на указанный. Если такого каталога не существует, возвращает false. Параметр $path может определять и относительный путь, задающийся от текущего каталога. Вот несколько примеров:

chdir("/tmp/data"); // переходим по абсолютному пути

chdir("./somathing"); // переходим в подкаталог текущего каталога

chdir("something"); // то же самое

chdir(".."); // переходим в родительский каталог

chdir("~/data"); // переходим в /home/ПОЛЬЗОВАТЕЛЬ/data (для Unix)

string getcwd()

Возвращает полный путь к текущему каталогу, начиная от "корня" (/). Если такой путь не может быть отслежен (это иногда бывает в Unix из-за того, что права на чтение для родительских каталогов могут быть сняты), вызов "проваливается" и возвращает false. Эта функция появилась в PHP совсем недавно. Так что если ее не окажется в вашей версии, обновите ее поскорее, либо напишите заменитель (что не так-то просто).

Работа с записями

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

int opendir(string $path)

Открывает каталог $path для дальнейшего считывания из него информации о файлах и подкаталогах и возвращает его идентификатор. Дальнейшие вызовы readdir() с идентификатором в параметрах будут обращены именно к этому каталогу. Функция возвращает false, если произошла ошибка.

string readdir(int $handle)

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

(ссылка на текущий каталог) и .. (ссылка на родительский каталог).

В подавляющем большинстве случаев нам нужно их игнорировать, что и сделано в примере из листинга 12.1 при помощи инструкции continue. PHP версии 3 позволял опускать параметр $handle – в этом случае, кажется, подразумевался последний открытый каталог. Сценарий "собирался с силами", "вздыхал" и… кое-как работал. PHP версии 4 более строг: в нем вы обязательно должны указывать параметр $handle для функции readdir(), в противном случае вам гарантированы сюрпризы. В случае, если в каталоге все файлы уже считаны, функция возвращает ложное значение. Но не позволяйте себе привыкнуть к конструкции такого вида:

$d=opendir("somewhere");

while($e=readdir($d)) { . . .}

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

$d=opendir("somewhere");

while(($e=readdir($d))!==false) { . . .}

Оператор !== позволяет точно проверить, была ли возвращена величина false.

void closedir(int $handle)

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

void rewinddir(int $handle)

"Перематывает" внутренний указатель открытого каталога на начало. После этого можно воспользоваться readdir(), чтобы заново начать считывать содержимое каталога.

Пример: печать дерева каталогов

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

Листинг 12.1. Печать дерева каталогов в файловой системе

<?

// Функция распечатывает имена всех подкаталогов в текущем каталоге,

// выполняя рекурсивный обход. Параметр $level задает текущую

// глубину рекурсии.

function PrintTree($level=1)

{

// Открываем каталог и выходим в случае ошибки

$d=@opendir(".");

if(!$d) return;

while(($e=readdir($d))!==false) {

// Игнорируем элементы .. и .

if($e=='.'||$e=='..') continue;

// Нам нужны только подкаталоги

if(!@is_dir($e)) continue;

// Печатаем пробелы, чтобы сместить вывод

for($i=0; $i<$level; $i++) echo " ";

// Выводим текущий элемент

echo "$e\n";

// Входим в текущий подкаталог и печатаем его

if(!chdir($e)) continue;

PrintTree($level+1);

// Возвращаемся назад

chdir("..");

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

// для больших распечаток

flush();

}

closedir($d);

}

// Выводим остальнойтекст фиксированным шрифтом

echo "<pre>";

echo "/\n";

// Входим в корневой каталог и печатаем его

chdir("/");

PrintTree();

echo "</pre>";

?>

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