您要查找的也称为递归目录遍历。这意味着,您将浏览所有目录并列出其中的子目录和文件。如果有一个子目录,它也会被遍历等等 - 所以它是递归的。
正如您想象的那样,这是您编写软件时所需要的一种常见的东西,而 PHP 支持您这样做。它提供了一个RecursiveDirectoryIterator,以便目录可以递归迭代,标准RecursiveIteratorIterator 进行遍历。然后,您可以通过简单的迭代轻松访问所有文件和目录,例如通过foreach:
$rootpath = '.';
$fileinfos = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootpath)
);
foreach($fileinfos as $pathname => $fileinfo) {
if (!$fileinfo->isFile()) continue;
var_dump($pathname);
}
这个例子首先指定了你要遍历的目录。我一直在服用当前的:
$rootpath = '.';
下一行代码有点长,它先实例化the directory iterator,然后再实例化the iterator-iterator,这样树状结构就可以在单/扁平循环中遍历:
$fileinfos = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootpath)
);
这些$fileinfos 然后用一个简单的foreach 进行迭代:
foreach($fileinfos as $pathname => $fileinfo) {
在其中,有一个测试可以跳过所有目录的输出。这是通过使用迭代的SplFileInfo 对象来完成的。它由递归目录迭代器提供,在处理文件时包含许多有用的属性和方法。例如,您还可以返回文件扩展名、有关大小和时间的基本名称信息等等。
if (!$fileinfo->isFile()) continue;
最后我只输出 pathname 即文件的完整路径:
var_dump($pathname);
示例输出如下所示(此处为 Windows 操作系统):
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 时,可以使用一些标志来控制此行为:
$fileinfos = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('.'),
RecursiveIteratorIterator::LEAVES_ONLY,
RecursiveIteratorIterator::CATCH_GET_CHILD
);
我希望这能提供信息。您也可以将其封装到您自己的类中,还可以提供FilterIterator 以将是否应列出文件的决定移出foreach 循环。
RecursiveDirectoryIterator 和 RecursiveIteratorIterator 组合的强大之处在于其灵活性。上面没有提到的是所谓的FilterIterators。我想我添加另一个示例,即利用两个自写的它们,将它们相互放置以组合它们。
- 一种方法是过滤掉所有以点开头的文件和目录(在 UNIX 系统上这些文件和目录被视为隐藏文件,因此您不应将这些信息提供给外部)和
- 另一个将列表过滤为文件。那是以前在foreach 内部进行的检查。
此用法示例的另一个更改是使用 getSubPathname() function,它返回从迭代的根路径开始的子路径,因此是您要查找的路径。
我还明确添加了 SKIP_DOTS flag 以防止遍历 . 和 .. (技术上不是真的必要的,因为过滤器会过滤它们以及它们是目录,但我认为它更正确)并作为UNIX_PATHS 的路径返回,因此无论底层操作系统如何,路径字符串始终是类似unix的路径,如果稍后通过HTTP请求这些值,通常是一个好主意,就像你的情况一样:
$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";
}
此示例与上一个示例类似,尽管 $fileinfos 的构建方式略有不同。特别是关于过滤器的部分是新的:
new FilesOnlyFilter(
new VisibleOnlyFilter(
new RecursiveDirectoryIterator($rootpath, ...)
)
),
所以目录迭代器被放入一个过滤器,过滤器本身被放入另一个过滤器。其余的没有变化。
这些过滤器的代码非常简单,它们与accept 函数一起使用,即true 或false,用于获取或过滤掉:
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();
}
}
又是这样。当然,您也可以将这些过滤器用于其他情况。例如。如果您有另一种目录列表。
还有另一个示例性输出,其中 $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 等文件列表。
FilesOnlyFilter 和 LEAVES_ONLY 的注意事项:
过滤器基于SplFileInfo 对象(only regular files that do exist)明确拒绝使用目录和链接。所以这是一个真正的基于文件系统的过滤。
另一种仅获取非目录条目的方法随 RecursiveIteratorIterator 一起提供,因为默认的 LEAVES_ONLY flag(此处也用于示例)。此标志不能用作过滤器,并且独立于底层迭代器。它只是指定迭代不应该返回分支(here: 目录迭代器的情况下)。