使用PHP递归函数列出目录中的所有文件和文件夹

120

我正在尝试遍历目录中的所有文件,如果有一个目录,就会遍历它的所有文件,直到没有更多目录可供访问为止。每个处理过的项目都将添加到下面函数中的结果数组中。但它并没有起作用,我不确定我该怎么做/我做错了什么,但是当处理以下代码时,浏览器运行速度极慢,感谢您的帮助!

代码:

    function getDirContents($dir){
        $results = array();
        $files = scandir($dir);

            foreach($files as $key => $value){
                if(!is_dir($dir. DIRECTORY_SEPARATOR .$value)){
                    $results[] = $value;
                } else if(is_dir($dir. DIRECTORY_SEPARATOR .$value)) {
                    $results[] = $value;
                    getDirContents($dir. DIRECTORY_SEPARATOR .$value);
                }
            }
    }

    print_r(getDirContents('/xampp/htdocs/WORK'));

11
RecursiveDirectoryIterator - u_mulder
20个回答

197

获取目录中的所有文件和文件夹,在有...时不要调用函数。

您的代码:

<?php
function getDirContents($dir, &$results = array()) {
    $files = scandir($dir);

    foreach ($files as $key => $value) {
        $path = realpath($dir . DIRECTORY_SEPARATOR . $value);
        if (!is_dir($path)) {
            $results[] = $path;
        } else if ($value != "." && $value != "..") {
            getDirContents($path, $results);
            $results[] = $path;
        }
    }

    return $results;
}

var_dump(getDirContents('/xampp/htdocs/WORK'));

输出(示例):

array (size=12)
  0 => string '/xampp/htdocs/WORK/iframe.html' (length=30)
  1 => string '/xampp/htdocs/WORK/index.html' (length=29)
  2 => string '/xampp/htdocs/WORK/js' (length=21)
  3 => string '/xampp/htdocs/WORK/js/btwn.js' (length=29)
  4 => string '/xampp/htdocs/WORK/js/qunit' (length=27)
  5 => string '/xampp/htdocs/WORK/js/qunit/qunit.css' (length=37)
  6 => string '/xampp/htdocs/WORK/js/qunit/qunit.js' (length=36)
  7 => string '/xampp/htdocs/WORK/js/unit-test.js' (length=34)
  8 => string '/xampp/htdocs/WORK/xxxxx.js' (length=30)
  9 => string '/xampp/htdocs/WORK/plane.png' (length=28)
  10 => string '/xampp/htdocs/WORK/qunit.html' (length=29)
  11 => string '/xampp/htdocs/WORK/styles.less' (length=30)

如果我们递归扫描目录(以避免在输出中出现文件夹路径),我认为应该将此条件添加到“else if”中:getDirContents($path, $results); if (!is_dir($path)) { $results[] = $path; } - Andrew

152
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('path/to/folder'));
$files = array(); 

/** @var SplFileInfo $file */
foreach ($rii as $file) {
    if ($file->isDir()){ 
        continue;
    }
         
    $files[] = $file->getPathname();        
}

var_dump($files);
    

这将为您提供所有带路径的文件。


有没有不使用内置对象的方法来完成这个任务? - user3412869
4
你可以反转条件语句:if (!$file->isDir()) $files[] = $file->getPathname();,以节省一行代码。 - user2226755
15
可以使用 RecursiveDirectoryIterator::SKIP_DOTS 来避免继续搜索 "." 和 ".." 目录。 - Razvan Grigore
将foreach更改为:$Regex = new RegexIterator($rii, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH); - xayer
@RazvanGrigore 我不确定这对非 ... 目录有什么帮助。你还是需要过滤掉它们吗? - War10ck

33

13

如果您希望将目录内容作为数组获取,而忽略隐藏的文件和目录,则此方法可能有所帮助。

function dir_tree($dir_path)
{
    $rdi = new \RecursiveDirectoryIterator($dir_path);

    $rii = new \RecursiveIteratorIterator($rdi);

    $tree = [];

    foreach ($rii as $splFileInfo) {
        $file_name = $splFileInfo->getFilename();

        // Skip hidden files and directories.
        if ($file_name[0] === '.') {
            continue;
        }

        $path = $splFileInfo->isDir() ? array($file_name => array()) : array($file_name);

        for ($depth = $rii->getDepth() - 1; $depth >= 0; $depth--) {
            $path = array($rii->getSubIterator($depth)->current()->getFilename() => $path);
        }

        $tree = array_merge_recursive($tree, $path);
    }

    return $tree;
}

结果会是这样的:

dir_tree(__DIR__.'/public');

[
    'css' => [
        'style.css',
        'style.min.css',
    ],
    'js' => [
        'script.js',
        'script.min.js',
    ],
    'favicon.ico',
]

来源


12

获取目录中带有过滤器(第二个参数)的所有文件和文件夹,在具有 ... 的情况下不要调用函数。

你的代码:

<?php
function getDirContents($dir, $filter = '', &$results = array()) {
    $files = scandir($dir);

    foreach($files as $key => $value){
        $path = realpath($dir.DIRECTORY_SEPARATOR.$value); 

        if(!is_dir($path)) {
            if(empty($filter) || preg_match($filter, $path)) $results[] = $path;
        } elseif($value != "." && $value != "..") {
            getDirContents($path, $filter, $results);
        }
    }

    return $results;
} 

// Simple Call: List all files
var_dump(getDirContents('/xampp/htdocs/WORK'));

// Regex Call: List php files only
var_dump(getDirContents('/xampp/htdocs/WORK', '/\.php$/'));

输出(示例):

// Simple Call
array(13) {
  [0]=> string(69) "/xampp/htdocs/WORK.htaccess"
  [1]=> string(73) "/xampp/htdocs/WORKConverter.php"
  [2]=> string(69) "/xampp/htdocs/WORKEvent.php"
  [3]=> string(70) "/xampp/htdocs/WORKdefault_filter.json"
  [4]=> string(68) "/xampp/htdocs/WORKdefault_filter.xml"
  [5]=> string(80) "/xampp/htdocs/WORKCaching/ApcCache.php"
  [6]=> string(84) "/xampp/htdocs/WORKCaching/CacheFactory.php"
}

// Regex Call
array(13) {
  [0]=> string(69) "/xampp/htdocs/WORKEvent.php"
  [1]=> string(73) "/xampp/htdocs/WORKConverter.php"
  [2]=> string(80) "/xampp/htdocs/WORKCaching/ApcCache.php"
  [3]=> string(84) "/xampp/htdocs/WORKCaching/CacheFactory.php"
}

詹姆斯·卡梅隆的提议。


10

我提出的方案是不需要丑陋的"foreach"控制结构的

$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
$allFiles = array_filter(iterator_to_array($iterator), fn($file) $file->isFile());

你可能只想提取文件路径,可以按照以下方式进行:

array_keys($allFiles);

仍然是4行代码,但比使用循环或其他方法更加直接。


5

以下是我想出的代码,而且行数很少:

function show_files($start) {
    $contents = scandir($start);
    array_splice($contents, 0,2);
    echo "<ul>";
    foreach ( $contents as $item ) {
        if ( is_dir("$start/$item") && (substr($item, 0,1) != '.') ) {
            echo "<li>$item</li>";
            show_files("$start/$item");
        } else {
            echo "<li>$item</li>";
        }
    }
    echo "</ul>";
}

show_files('./');

输出结果类似于

..idea
.add.php
.add_task.php
.helpers
 .countries.php
.mysqli_connect.php
.sort.php
.test.js
.test.php
.view_tasks.php

这些点是无序列表的点。

希望这可以帮到你。


4

添加相对路径选项:

function getDirContents($dir, $relativePath = false)
{
    $fileList = array();
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
    foreach ($iterator as $file) {
        if ($file->isDir()) continue;
        $path = $file->getPathname();
        if ($relativePath) {
            $path = str_replace($dir, '', $path);
            $path = ltrim($path, '/\\');
        }
        $fileList[] = $path;
    }
    return $fileList;
}

print_r(getDirContents('/path/to/dir'));

print_r(getDirContents('/path/to/dir', true));

输出:

Array
(
    [0] => /path/to/dir/test1.html
    [1] => /path/to/dir/test.html
    [2] => /path/to/dir/index.php
)

Array
(
    [0] => test1.html
    [1] => test.html
    [2] => index.php
)

3
这是Hors答案的修改版,适用于我的情况,因为它会在传递时剥离掉基本目录,并且具有可以设置为false的递归开关,这也非常方便。此外,为了使输出更易读,我将文件和子目录文件分开,先添加文件,然后添加子目录文件(请参见结果)。 我尝试了一些其他方法和建议,最终得到了这个结果。我已经有另一种工作方法,非常相似,但似乎在没有文件的子目录中失败了,但该子目录有一个带有文件的子子目录,它不扫描子子目录以查找文件-因此有些答案可能需要测试这种情况。无论如何,我也想在这里张贴我的版本,以防有人在寻找...
function get_filelist_as_array($dir, $recursive = true, $basedir = '', $include_dirs = false) {
    if ($dir == '') {return array();} else {$results = array(); $subresults = array();}
    if (!is_dir($dir)) {$dir = dirname($dir);} // so a files path can be sent
    if ($basedir == '') {$basedir = realpath($dir).DIRECTORY_SEPARATOR;}

    $files = scandir($dir);
    foreach ($files as $key => $value){
        if ( ($value != '.') && ($value != '..') ) {
            $path = realpath($dir.DIRECTORY_SEPARATOR.$value);
            if (is_dir($path)) {
                // optionally include directories in file list
                if ($include_dirs) {$subresults[] = str_replace($basedir, '', $path);}
                // optionally get file list for all subdirectories
                if ($recursive) {
                    $subdirresults = get_filelist_as_array($path, $recursive, $basedir, $include_dirs);
                    $results = array_merge($results, $subdirresults);
                }
            } else {
                // strip basedir and add to subarray to separate file list
                $subresults[] = str_replace($basedir, '', $path);
            }
        }
    }
    // merge the subarray to give the list of files then subdirectory files
    if (count($subresults) > 0) {$results = array_merge($subresults, $results);}
    return $results;
}

我认为在调用该函数时,需要注意的一件事情是不要将$basedir值传递给它...通常只需传递$dir(或现在也可以传递文件路径),并根据需要选择性地将$recursive设置为false。结果如下:

[0] => demo-image.png
[1] => filelist.php
[2] => tile.png
[3] => 2015\header.png
[4] => 2015\08\background.jpg

享受吧!好的,回到我实际使用这个程序的地方...

更新 添加了一个额外的参数来决定是否在文件列表中包含目录(记住需要传递其他参数才能使用此功能)。例如:

$results = get_filelist_as_array($dir, true, '', true);


2
这个解决方案对我很有用。RecursiveIteratorIterator递归列出所有的目录和文件,但是没有排序。程序筛选列表并进行排序。
我相信有一种方式可以更短地编写它;随意改进它。 这只是一段代码片段。您可能想将其定制为您的目的。
<?php

$path = '/pth/to/your/directories/and/files';
// an unsorted array of dirs & files
$files_dirs = iterator_to_array( new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path),RecursiveIteratorIterator::SELF_FIRST) );

echo '<html><body><pre>';
// create a new associative multi-dimensional array with dirs as keys and their files
$dirs_files = array();
foreach($files_dirs as $dir){
 if(is_dir($dir) AND preg_match('/\/\.$/',$dir)){
  $d = preg_replace('/\/\.$/','',$dir);
  $dirs_files[$d] = array();
  foreach($files_dirs as $file){
   if(is_file($file) AND $d == dirname($file)){
    $f = basename($file);
    $dirs_files[$d][] = $f;
   }
  }
 }
}
//print_r($dirs_files);

// sort dirs
ksort($dirs_files);

foreach($dirs_files as $dir => $files){
 $c = substr_count($dir,'/');
 echo  str_pad(' ',$c,' ', STR_PAD_LEFT)."$dir\n";
 // sort files
 asort($files);
 foreach($files as $file){
  echo str_pad(' ',$c,' ', STR_PAD_LEFT)."|_$file\n";
 }
}
echo '</pre></body></html>';

?>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接