Javascript-форум (https://javascript.ru/forum/)
-   Серверные языки и технологии (https://javascript.ru/forum/server/)
-   -   Работа с каталогами PHP (https://javascript.ru/forum/server/85852-rabota-s-katalogami-php.html)

firep91613 17.04.2024 17:06

Работа с каталогами PHP
 
function printTree(int $level = 1) : void {
	$dir = opendir('.');

	if (!$dir) return;

	while (($file = readdir($dir)) !== false) {
		if ($file == '.' || $file == '..') continue;
		if (!is_dir($file)) continue;

		for ($i = 0; $i < $level; $i++) echo ' ';
		echo $file . PHP_EOL;

		if (!chdir($file)) continue;

		printTree($level + 1);
		chdir('..');
		flush();
	}

	closedir($dir);
}

// Структура каталогов:
main_catalog
   sub_catalog
      file1.php
      file2.php
   next_sub_folder

В $dir, при запуске функции - дескриптор main_catalog


Не понятно, что происходит, когда функция запрыгнет в sub_catalog и разшарит все файлы там. Директория будет закрыта, дальше управление перейдет во внешний вызов сюда: chdir('..') - выход наверх в main_catalog. Во внешнем вызове, на следующей итерции while в $file будет содержатся next_sub_folder.

Как связан chdir('..') и то что содержится в $file? Ну то есть в первом вызове readdir($dir) возвращает все содержимое main_catalog, потом, после выпрыгивания из sub_catalog, на следующей итерации while readdir($dir) возвращает следующую директорию. Не могу точно сформулировать, chdir('..') влияет на эту переменную $dir?

Nexus 17.04.2024 20:29

function printTree(int $level = 1) : void {
    // открываем дескриптор текущей директории
    $dir = opendir('.');
 
    // завершаем работу функции, если не удалось открыть дескриптор
    if (!$dir) return;
 
    // перебираем элементы директории по её дескриптору
    while (($file = readdir($dir)) !== false) {
        // если это ссылка на текущую директорию или родительскую - игнорируем этот элемент
        if ($file == '.' || $file == '..') continue;
        // если это не директория - игнорируем этот элемент
        if (!is_dir($file)) continue;
 
        // выводим "отступ" от левого края
        for ($i = 0; $i < $level; $i++) echo ' ';
        // выводим название директории
        echo $file . PHP_EOL;
 
        // пытаемся изменить текущую директорию на дочернюю директорию $file
        // если не удалось - пропускаем директорию $file
        if (!chdir($file)) continue;
 
        // рекурсивно вызываем эту же функцию, чтобы она 
        // отобразила "дерево" каталогов, но уже для дочерней директории $file
        printTree($level + 1);
        // изменяем текущую директорию на изначальную
        chdir('..');
        // непонятно зачем, но пытаемся сбросить буфер вывода
        flush();
    }
 
    // закрываем дескриптор
    closedir($dir);
}

firep91613 18.04.2024 12:36

Nexus,
это все так. Непонятно просто как под капотом работает. Когда будет выход из дочерней директории цикл во внешнем вызове после смены директории на изначальную запоминает на каком месте остановился? Видимо запоминает, без chdir('..') не работает.

Nexus 18.04.2024 13:11

Цитата:

Сообщение от firep91613
Когда будет выход из дочерней директории цикл во внешнем вызове после смены директории на изначальную запоминает на каком месте остановился?

Да, запоминается, но это вовсе не заслуга chdir.
Дескриптор родительского каталога не закрывается до тех пор, пока все директории изначального каталога не будут перечислены.

firep91613 18.04.2024 16:42

Цитата:

Сообщение от Nexus
Дескриптор родительского каталога не закрывается до тех пор, пока все директории изначального каталога не будут перечислены.

Ну да. Ну все равно, какая-то связь между откртым дескриптором и chdir есть. Без chdir просто не работает.

Nexus 18.04.2024 19:25

Цитата:

Сообщение от firep91613
Ну все равно, какая-то связь между откртым дескриптором и chdir есть.

chdir - изменяет текущую директорию на указанную первым аргументом.
opendir - открывает дескриптор указанной первым аргументом директории.
Без chdir не работает, поскольку функция пытается получить исключительно дескриптор текущей директории (working directory).

Эту функцию можно переписать и без использования chdir, передавая функции printTree путь до каталога, дерево которого нужно вывести.

function printTree(string $path): void {
    $handle = opendir($path);
    if (!$handle) {
        return;
    }

    // and so on
}

firep91613 19.04.2024 12:45

Nexus,
понятно, спасибо.


Часовой пояс GMT +3, время: 07:46.