且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

PHP获取路径文件夹/子文件夹中的所有文件到数组?

更新时间:2023-01-15 20:52:00

你所寻找的是也叫的递归遍历目录的。这意味着,你正在经历的所有目录,并列出子目录和文件在那里。如果它被遍历以及等等等等子目录 - 所以它是递归的

What you are looking for is also called recursive directory traversing. Which means, you're going through all directories and list subdirectories and files in there. If there is a subdirectory it is traversed as well and so on and so forth - so it is recursive.

你可以想象这是有点当你写一个软件,你需要和PHP支持你与常见的事。它提供有一个 RecursiveDirectoryIterator 这样的目录可以递归迭代和标准 RecursiveIteratorIterator 做遍历。然后,您可以轻松地访问所有文件和目录用一个简单的迭代,例如,通过的foreach

As you can imagine this is somewhat a common thing you need when you write a software and PHP supports you with that. It offers one RecursiveDirectoryIterator so that directories can be recursively iterated and the standard RecursiveIteratorIterator to do the traversal. You can then easily access all files and directories with a simple iteration, for example via foreach:

$rootpath = '.';
$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootpath)
);
foreach($fileinfos as $pathname => $fileinfo) {
    if (!$fileinfo->isFile()) continue;
    var_dump($pathname);
}

这个例子首先指定要遍历目录。我一直在服用当前的:

This example first of all specifies the directory you want to traverse. I've been taking the current one:

$rootpath = '.';

code的下一行是有点长,但它确实实例目录迭代器,然后的the迭代器迭代器从而使树形结构可以在单个/平的循环来遍历:

The next line of code is a little bit long, it does instantiate the directory iterator and then the iterator-iterator so that the tree-like structure can be traversed in a single/flat loop:

$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootpath)
);

这些 $ fileinfos 然后用重复一个简单的的foreach

These $fileinfos are then iterated with a simple foreach:

foreach($fileinfos as $pathname => $fileinfo) {

里面的话,有一个测试,被输出跳过所有目录。这是通过使用 SplFileInfo 是迭代对象来完成。它是由递归迭代目录提供,处理文件时,包含了很多有用的属性和方法。您可以同时例如返回文件扩展名,关于大小和时间等等等等基名信息。

Inside of it, there is a test to skip all directories from being output. This is done by using the SplFileInfo object that is iterated over. It is provided by the recursive directory iterator and contains a lot of helpful properties and methods when working with files. You can as well for example return the file extension, the basename information about size and time and so on and so forth.

if (!$fileinfo->isFile()) continue;

最后,我只输出的路径的就是该文件的完整路径:

Finally I just output the pathname that is the full path to the file:

var_dump($pathname);

这是典型的输出应该是这样的(这里是Windows操作系统上):

An exemplary output would look like this (here on a windows operating system):

string(12) ".\.buildpath"
string(11) ".\.htaccess"
string(33) ".\dom\xml-attacks\attacks-xml.php"
string(38) ".\dom\xml-attacks\billion-laughs-2.xml"
string(36) ".\dom\xml-attacks\billion-laughs.xml"
string(40) ".\dom\xml-attacks\quadratic-blowup-2.xml"
string(40) ".\dom\xml-attacks\quadratic-blowup-3.xml"
string(38) ".\dom\xml-attacks\quadratic-blowup.xml"
string(22) ".\dom\xmltree-dump.php"
string(25) ".\dom\xpath-list-tags.php"
string(22) ".\dom\xpath-search.php"
string(27) ".\dom\xpath-text-search.php"
string(29) ".\encrypt-decrypt\decrypt.php"
string(29) ".\encrypt-decrypt\encrypt.php"
string(26) ".\encrypt-decrypt\test.php"
string(13) ".\favicon.ico"

如果有一个子目录是无法访问的,下面会抛出异常。这种行为可以通过一些标志实例化时被控制的 RecursiveIteratorIterator

If there is a subdirectory that is not accessible, the following would throw an exception. This behaviour can be controlled with some flags when instantiating the RecursiveIteratorIterator:

$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('.'),
    RecursiveIteratorIterator::LEAVES_ONLY,
    RecursiveIteratorIterator::CATCH_GET_CHILD
);

我希望这是翔实。您也可以总结这件事到一个类你自己的,你也可以提供一个 FilterIterator 来移动一个文件是否应该上市还是决定不出去了的foreach 循环。

I hope this was informative. You can also Wrap this up into a class of your own and you can also provide a FilterIterator to move the decision whether a file should be listed or not out of the foreach loop.

RecursiveDirectoryIterator RecursiveIteratorIterator 组合的力量散发出来的灵活性。上面有什么不涵盖的所谓 FilterIterator 秒。我想我补充一点,是利用两个自写他们,放入对方把它们结合起来的另一个例子。

The power of the RecursiveDirectoryIterator and RecursiveIteratorIterator combination comes out of its flexibility. What was not covered above are so called FilterIterators. I thought I add another example that is making use of two self-written of them, placed into each other to combine them.


  • 一个是过滤掉以点开始(那些被认为是在UNIX系统中的隐藏文件,所以你不应该放弃这些信息外),所有的文件和目录

  • 另外一个是过滤列表的文件。这是检查previously是的 的内部的foreach。

  • One is to filter out all files and directories that start with a dot (those are considered hidden files on UNIX systems so you should not give that information to the outside) and
  • Another one that is filtering the list to files only. That is the check that previously was inside the foreach.

在此使用示例的另一个变化是利用的getSubPathname()$c$c>函数返回从迭代的ROOTPATH​​启动子路径,所以你要找的人。

Another change in this usage example is to make use of the getSubPathname() function that returns the subpath starting from the iteration's rootpath, so the one you're looking for.

此外,我明确地添加SKIP_DOTS$c$c>标志其中prevents穿越 .. (技术上没有的真正必要的,因为他们是目录过滤器将过滤那些为好,但我认为这是比较正确的),并作为返回路径,UNIX_PATHS$c$c>这样的路径字符串是不管底层操作系统这通常是一个好主意,如果通过HTTP请求这些值后来随着你的情况总是Unix类路径:

Also I explicitly add the SKIP_DOTS flag which prevents traversing . and .. (technically not really necessary because the filters would filter those as well as they are directories, however I think it is more correct) and return as paths as UNIX_PATHS so the strings of paths are always unix-like paths regardless of the underlying operating system Which is normally a good idea if those values are requested via HTTP later as in your case:

$rootpath = '.';

$fileinfos = new RecursiveIteratorIterator(
    new FilesOnlyFilter(
        new VisibleOnlyFilter(
            new RecursiveDirectoryIterator(
                $rootpath,
                FilesystemIterator::SKIP_DOTS
                    | FilesystemIterator::UNIX_PATHS
            )
        )
    ),
    RecursiveIteratorIterator::LEAVES_ONLY,
    RecursiveIteratorIterator::CATCH_GET_CHILD
);

foreach ($fileinfos as $pathname => $fileinfo) {
    echo $fileinfos->getSubPathname(), "\n";
}

这例子是类似previous 1虽然在 $ fileinfos 是构建是一个怎么有点不同的配置。尤其是关于过滤器的部分是新的:

This example is similar to the previous one albeit how the $fileinfos is build is a little differently configured. Especially the part about the filters is new:

    new FilesOnlyFilter(
        new VisibleOnlyFilter(
            new RecursiveDirectoryIterator($rootpath, ...)
        )
    ),

所以目录迭代被放入过滤器和过滤器自身被放入另一个滤波器。其余未发生变化。

So the directory iterator is put into a filter and the filter itself is put into another filter. The rest did not change.

在code这些滤波器是pretty直线前进,他们用接受函数,或者是真正工作code>或这是采取或过滤掉:

The code for these filters is pretty straight forward, they work with the accept function that is either true or false which is to take or to filter out:

class VisibleOnlyFilter extends RecursiveFilterIterator
{
    public function accept()
    {
        $fileName = $this->getInnerIterator()->current()->getFileName();
        $firstChar = $fileName[0];
        return $firstChar !== '.';
    }
}

class FilesOnlyFilter extends RecursiveFilterIterator
{
    public function accept()
    {
        $iterator = $this->getInnerIterator();

        // allow traversal
        if ($iterator->hasChildren()) {
            return true;
        }

        // filter entries, only allow true files
        return $iterator->current()->isFile();
    }
}

和一遍而已。当然,你可以使用这些过滤器的其他情况下,太。例如。如果你有另一种目录列表。

And that's it again. Naturally you can use these filters for other cases, too. E.g. if you have another kind of directory listing.

和另一典型输出 $ ROOTPATH​​ 切去:

test.html
test.rss
tests/test-pad-2.php
tests/test-pad-3.php
tests/test-pad-4.php
tests/test-pad-5.php
tests/test-pad-6.php
tests/test-pad.php
TLD/PSL/C/dkim-regdom.c
TLD/PSL/C/dkim-regdom.h
TLD/PSL/C/Makefile
TLD/PSL/C/punycode.pl
TLD/PSL/C/test-dkim-regdom.c
TLD/PSL/C/test-dkim-regdom.sh
TLD/PSL/C/tld-canon.h
TLD/PSL/generateEffectiveTLDs.php

没有更多的的.git 的.svn 目录遍历或文件的上市像。 builtpath 的.project

No more .git or .svn directory traversal or listing of files like .builtpath or .project.

注意的 FilesOnlyFilter LEAVES_ONLY
  
  该过滤器明确否认使用目录中的的基于 SplFileInfo 对象链接(的只有确实存在常规文件)。因此,它是基于文件系统上的实际滤波。结果
  只能获得非目录的另一种方法条目船,因为默认的 RecursiveIteratorIterator 。 construct.php> LEAVES_ONLY 标志(这里使用过的例子)。此标志不工作作为过滤器,并独立于底层的迭代器。它只是指定了迭代不应该返回枝条。(这里:的目录中的迭代器的情况下,目录)

Note for FilesOnlyFilter and LEAVES_ONLY: The filter explicitly denies the use of directories and links based on the SplFileInfo object (only regular files that do exist). So it is a real filtering based on the file-system.
Another method to only get non-directory entries ships with RecursiveIteratorIterator because of the default LEAVES_ONLY flag (here used too in the examples). This flag does not work as a filter and is independent to the underlying iterator. It just specifies that the iteration should not return branchs (here: directories in case of the directory iterator).