PHP сканирует рекурсивно

Я хочу, чтобы мой скрипт сканировал рекурсивно,

$files = scandir('/dir');
foreach($files as $file){
if(is_dir($file)){
    echo '<li><label class="tree-toggler nav-header"><i class="fa fa-folder-o"></i>'.$file.'</label>';
    $subfiles = scandir($rooturl.'/'.$file);
        foreach($subfiles as $subfile){
            // and so on and on and on
        }
        echo '<li>';
    } else {
        echo $file.'<br />';
    }
}

Я хотел бы зациклить это таким образом, чтобы для каждого каталога, найденного scandir, он запускал другой scandir в папках, которые были найдены в этом каталоге,

Таким образом, каталог «A» содержит каталог 1/2/3, теперь он должен сканировать (1), сканировать (2), сканировать (3) и так далее для каждого найденного каталога.

Как я могу реализовать это легко, не копируя код снова и снова в каждом foreach?

РЕДАКТИРОВАТЬ: Поскольку ответы почти такие же, как я уже пробовал, я немного обновлю вопрос.

С помощью этого скрипта мне нужно создать древовидный список. С текущими опубликованными скриптами происходит следующее:

/images/dir1/file1.png
/images/dir1/file2.png
/images/dir1/file3.png
/images/anotherfile.php
/data/uploads/avatar.jpg
/data/config.php
index.php

Что мне на самом деле нужно:

<li><label>images</label>
    <ul>
        <li><label>dir1</label>
            <ul>
                <li>file1.png</li>
                <li>file2.png</li>
                <li>file3.png</li>
            </ul>
        </li>
        <li>anotherfile.php</li>
    </ul>
</li>
<li><label>data</label>
    <ul>
        <li><label>uploads</label>
            <ul>
                <li>avatar.jpg</li>
            </ul>
        </li>
        <li>config.php</li>
    </ul>
</li>
<li>index.php</li>

И так далее, Спасибо за уже размещенные ответы!


person GRX    schedule 09.12.2015    source источник
comment
Помимо основ рекурсивного программирования, is_dir($file) должен быть is_dir(/dir/$file)   -  person Jack    schedule 10.12.2015
comment
ознакомьтесь с recursiveIterator набором классов - php.net/manual/en/class.recursivedirectoryiterator. php   -  person Professor Abronsius    schedule 10.12.2015


Ответы (5)


Вы можете рекурсивно сканировать каталог таким образом, что целью является ваш самый верхний каталог:

function scanDir($target) {

        if(is_dir($target)){

            $files = glob( $target . '*', GLOB_MARK ); //GLOB_MARK adds a slash to directories returned

            foreach( $files as $file )
            {
                scanDir( $file );
            }


        } 
    }

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

function delete_files($target) {

        if(is_dir($target)){

            $files = glob( $target . '*', GLOB_MARK ); //GLOB_MARK adds a slash to directories returned

            foreach( $files as $file )
            {
                delete_files( $file );
            }

            rmdir( $target );

        } elseif(is_file($target)) {

            unlink( $target );
    }

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

function assetsMap($source_dir, $directory_depth = 0, $hidden = FALSE)
    {
        if ($fp = @opendir($source_dir))
        {
            $filedata   = array();
            $new_depth  = $directory_depth - 1;
            $source_dir = rtrim($source_dir, '/').'/';

            while (FALSE !== ($file = readdir($fp)))
            {
                // Remove '.', '..', and hidden files [optional]
                if ( ! trim($file, '.') OR ($hidden == FALSE && $file[0] == '.'))
                {
                    continue;
                }

                if (($directory_depth < 1 OR $new_depth > 0) && @is_dir($source_dir.$file))
                {
                    $filedata[$file] = assetsMap($source_dir.$file.'/', $new_depth, $hidden);
                }
                else
                {
                    $filedata[] = $file;
                }
            }

            closedir($fp);
            return $filedata;
        }
        echo 'can not open dir';
        return FALSE;
    }

Передайте свой путь к функции:

$path = 'elements/images/';
$filedata = assetsMap($path, $directory_depth = 5, $hidden = FALSE);

$filedata — это массив со всеми основанными каталогами и подкаталогами с их содержимым. Эта функция позволяет вам сканировать структуру каталогов ($directory_depth) настолько глубоко, насколько вы хотите, а также избавиться от всех скучных скрытых файлов (например, '.', '..')

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

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

Я надеюсь, что это поможет вам, и я желаю вам счастливого Рождества.

person Franco    schedule 09.12.2015
comment
Спасибо за ответ, однако я не могу заставить это работать так, как мне нужно, я обновил свой первоначальный вопрос для лучшего объяснения :) - person GRX; 10.12.2015
comment
Я обязательно попробую это, я еще не проверял сценарии с открытым исходным кодом для этого, так как в основном вам нужно разрезать около 70% их сборок, чтобы они соответствовали вашим собственным сценариям/шаблонам/страницам. Спасибо за ответы и себя тоже с Рождеством! :) - person GRX; 10.12.2015
comment
Ваша функция сканирования каталога не возвращает никакого значения, и вы не можете вызвать функцию с этим именем, это зарезервированное имя. - person Froggiz; 25.04.2020

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

function scanAllDir($dir) {
  $result = [];
  foreach(scandir($dir) as $filename) {
    if ($filename[0] === '.') continue;
    $filePath = $dir . '/' . $filename;
    if (is_dir($filePath)) {
      foreach (scanAllDir($filePath) as $childFilename) {
        $result[] = $filename . '/' . $childFilename;
      }
    } else {
      $result[] = $filename;
    }
  }
  return $result;
}
person Allain Lalonde    schedule 11.10.2017
comment
Вы можете захотеть использовать константу DIRECTORY_SEPARATOR вместо жестко заданного '/', чтобы безопасно использовать код как в Linux, так и в системах Windows. - person Select0r; 04.10.2018
comment
По какой-то причине он выбирает только один подкаталог и рекурсивно сканирует его. - person JackTheKnife; 04.10.2019

Хотя вопрос старый. Но мой ответ может помочь людям, которые посещают этот вопрос.

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

global $file_info; // All the file paths will be pushed here
$file_info = array();

/**
 * 
 * @function recursive_scan
 * @description Recursively scans a folder and its child folders
 * @param $path :: Path of the folder/file
 * 
 * */
function recursive_scan($path){
    global $file_info;
    $path = rtrim($path, '/');
    if(!is_dir($path)) $file_info[] = $path;
        else {
            $files = scandir($path);
            foreach($files as $file) if($file != '.' && $file != '..') recursive_scan($path . '/' . $file);
        }
}

recursive_scan('/var/www/html/wp-4.7.2/wp-content/plugins/site-backup');
print_r($file_info);
person Aftabul Islam    schedule 05.03.2017

Создайте функцию сканирования и назовите ее рекурсивно...

e.g:

   <?php

    function scandir_rec($root)
    {
        echo $root . PHP_EOL;
        // When it's a file or not a valid dir name
        // Print it out and stop recusion 
        if (is_file($root) || !is_dir($root)) {
            return;
        }

        // starts the scan
        $dirs = scandir($root);
        foreach ($dirs as $dir) {
            if ($dir == '.' || $dir == '..') {
                continue; // skip . and ..
            }

            $path = $root . '/' . $dir;
            scandir_rec($path); // <--- CALL THE FUNCTION ITSELF TO DO THE SAME THING WITH SUB DIRS OR FILES.
        }
    }

    // run it when needed
    scandir_rec('./rootDir');

Вы можете сделать множество вариаций этой функции. Например, печать тега «li» вместо PHP_EOL для создания древовидного представления.

[РЕДАКТИРОВАТЬ]

 <?php

function scandir_rec($root)
{
    // if root is a file
    if (is_file($root)) {
        echo '<li>' . basename($root) . '</li>';
        return;
    }

    if (!is_dir($root)) {
        return;
    }

    $dirs = scandir($root);
    foreach ($dirs as $dir) {
        if ($dir == '.' || $dir == '..') {
            continue;
        }

        $path = $root . '/' . $dir;
        if (is_file($path)) {
            // if file, create list item tag, and done.
            echo '<li>' . $dir . '</li>';
        } else if (is_dir($path)) {
            // if dir, create list item with sub ul tag
            echo '<li>';
            echo '<label>' . $dir . '</label>';
            echo '<ul>';
            scandir_rec($path); // <--- then recursion
            echo '</ul>';
            echo '</li>';
        }
    }
}

// init call
$rootDir = 'rootDir';
echo '<ul>';
scandir_rec($rootDir);
echo '</ul>';
person VVLeon    schedule 10.12.2015
comment
Спасибо за ответ, однако я не могу заставить это работать так, как мне нужно, я обновил свой первоначальный вопрос для лучшего объяснения :) - person GRX; 10.12.2015

function getFiles(string $directory, array $allFiles = []): array
{
    $files = array_diff(scandir($directory), ['.', '..']);

    foreach ($files as $file) {
        $fullPath = $directory. DIRECTORY_SEPARATOR .$file;

        if( is_dir($fullPath) )
            $allFiles += getFiles($fullPath, $allFiles);
        else
            $allFiles[] = $file;
    }

    return $allFiles;
}

Я знаю, что это старо, но я хотел показать немного другую версию других ответов. Использует array_diff для отбрасывания файла . и .. папки. Также оператор + для объединения 2 массивов (я редко вижу, что это используется, так что это может быть полезно для кого-то)

person matteoruda    schedule 29.07.2021