跳到内容

Finder 组件

编辑此页

Finder 组件根据不同的标准(名称、文件大小、修改时间等)通过直观的流畅接口查找文件和目录。

安装

1
$ composer require symfony/finder

注意

如果您在 Symfony 应用程序之外安装此组件,则必须在代码中 require vendor/autoload.php 文件,以启用 Composer 提供的类自动加载机制。阅读本文了解更多详情。

用法

Finder 类查找文件和/或目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Symfony\Component\Finder\Finder;

$finder = new Finder();
// find all files in the current directory
$finder->files()->in(__DIR__);

// check if there are any search results
if ($finder->hasResults()) {
    // ...
}

foreach ($finder as $file) {
    $absoluteFilePath = $file->getRealPath();
    $fileNameWithExtension = $file->getRelativePathname();

    // ...
}

$file 变量是 SplFileInfo 的实例,它扩展了 PHP 自己的 SplFileInfo,以提供处理相对路径的方法。

警告

Finder 对象不会自动重置其内部状态。这意味着如果您不想获得混合结果,则需要创建一个新实例。

搜索文件和目录

该组件提供了许多方法来定义搜索条件。它们都可以被链式调用,因为它们实现了一个 流畅接口

位置

位置是唯一强制性的条件。它告诉 finder 使用哪个目录进行搜索

1
$finder->in(__DIR__);

通过链式调用 in() 在多个位置进行搜索

1
2
3
4
5
// search inside *both* directories
$finder->in([__DIR__, '/elsewhere']);

// same as above
$finder->in(__DIR__)->in('/elsewhere');

使用 * 作为通配符,在与模式匹配的目录中搜索(每个模式必须解析为至少一个目录路径)

1
$finder->in('src/Symfony/*/*/Resources');

使用 exclude() 方法从匹配中排除目录

1
2
// directories passed as argument must be relative to the ones defined with the in() method
$finder->in(__DIR__)->exclude('ruby');

也可以忽略您没有权限读取的目录

1
$finder->ignoreUnreadableDirs()->in(__DIR__);

由于 Finder 使用 PHP 迭代器,您可以传递任何带有受支持的 URL 风格协议的 PHP 封装器 (ftp://, zlib://, 等等) 的 URL

1
2
3
4
5
// always add a trailing slash when looking for in the FTP root dir
$finder->in('ftp://example.com/');

// you can also look for in a FTP directory
$finder->in('ftp://example.com/pub/');

它也适用于用户定义的流

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\Finder\Finder;

// register a 's3://' wrapper with the official AWS SDK
$s3Client = new Aws\S3\S3Client([/* config options */]);
$s3Client->registerStreamWrapper();

$finder = new Finder();
$finder->name('photos*')->size('< 100K')->date('since 1 hour ago');
foreach ($finder->in('s3://bucket-name') as $file) {
    // ... do something with the file
}

另请参阅

阅读 PHP 流 文档,了解如何创建自己的流。

文件或目录

默认情况下,Finder 返回文件和目录。如果您只需要查找文件或目录,请使用 files()directories() 方法

1
2
3
4
5
// look for files only; ignore directories
$finder->files();

// look for directories only; ignore files
$finder->directories();

如果您想跟踪符号链接,请使用 followLinks() 方法

1
$finder->files()->followLinks();

请注意,此方法会跟踪链接,但不会解析它们。考虑以下文件或目录的结构

1
2
3
4
5
6
7
8
├── folder1/
│   ├──file1.txt
│   ├── file2link (symbolic link to folder2/file2.txt file)
│   └── folder3link (symbolic link to folder3/ directory)
├── folder2/
│   └── file2.txt
└── folder3/
    └── file3.txt

如果您尝试通过 $finder->files()->in('/path/to/folder1/') 查找 folder1/ 中的所有文件,您将获得以下结果

  • 使用 followLinks() 方法时:file1.txtfile2link(此链接未解析)。folder3link 不会出现在结果中,因为它没有被跟踪或解析;
  • 当使用 followLinks() 方法时:file1.txtfile2link(此链接仍然未解析)和 folder3/file3.txt(此文件出现在结果中,因为 folder1/folder3link 链接被跟踪)。

版本控制文件

版本控制系统 (简称 "VCS"),例如 Git 和 Mercurial,会创建一些特殊文件来存储其元数据。默认情况下,在查找文件和目录时会忽略这些文件,但您可以使用 ignoreVCS() 方法更改此设置

1
$finder->ignoreVCS(false);

如果搜索目录及其子目录包含 .gitignore 文件,您可以使用 ignoreVCSIgnored() 方法重用这些规则,从结果中排除文件和目录

1
2
// excludes files/directories matching the .gitignore patterns
$finder->ignoreVCSIgnored(true);

一个目录的规则始终覆盖其父目录的规则。

注意

Git 从仓库根目录开始查找 .gitignore 文件。Symfony 的 Finder 行为不同,它从用于搜索文件/目录的目录开始查找 .gitignore 文件。为了与 Git 行为保持一致,您应该显式地从 Git 仓库根目录进行搜索。

文件名

使用 name() 方法按名称查找文件

1
$finder->files()->name('*.php');

name() 方法接受 glob、字符串、正则表达式或 glob、字符串或正则表达式的数组

1
$finder->files()->name('/\.php$/');

可以通过链式调用或传递数组来定义多个文件名

1
2
3
4
$finder->files()->name('*.php')->name('*.twig');

// same as above
$finder->files()->name(['*.php', '*.twig']);

notName() 方法排除与模式匹配的文件

1
$finder->files()->notName('*.rb');

可以通过链式调用或传递数组来排除多个文件名

1
2
3
4
$finder->files()->notName('*.rb')->notName('*.py');

// same as above
$finder->files()->notName(['*.rb', '*.py']);

文件内容

使用 contains() 方法按内容查找文件

1
$finder->files()->contains('lorem ipsum');

contains() 方法接受字符串或正则表达式

1
$finder->files()->contains('/lorem\s+ipsum$/i');

notContains() 方法排除包含给定模式的文件

1
$finder->files()->notContains('dolor sit amet');

路径

使用 path() 方法按路径查找文件和目录

1
2
3
4
// matches files that contain "data" anywhere in their paths (files or directories)
$finder->path('data');
// for example this will match data/*.xml and data.xml if they exist
$finder->path('data')->name('*.xml');

在所有平台(包括 Windows)上,使用正斜杠(即 /)作为目录分隔符。该组件在内部进行必要的转换。

path() 方法接受字符串、正则表达式或字符串或正则表达式的数组

1
2
$finder->path('foo/bar');
$finder->path('/^foo\/bar/');

可以通过链式调用或传递数组来定义多个路径

1
2
3
4
$finder->path('data')->path('foo/bar');

// same as above
$finder->path(['data', 'foo/bar']);

在内部,字符串通过转义斜杠并添加分隔符转换为正则表达式

原始给定字符串 使用的正则表达式
dirname /dirname/
a/b/c /a\/b\/c/

notPath() 方法按路径排除文件

1
$finder->notPath('other/dir');

可以通过链式调用或传递数组来排除多个路径

1
2
3
4
$finder->notPath('first/dir')->notPath('other/dir');

// same as above
$finder->notPath(['first/dir', 'other/dir']);

文件大小

使用 size() 方法按大小查找文件

1
$finder->files()->size('< 1.5K');

通过链式调用或传递数组来限制大小范围

1
2
3
4
$finder->files()->size('>= 1K')->size('<= 2K');

// same as above
$finder->files()->size(['>= 1K', '<= 2K']);

比较运算符可以是以下任何一种:>>=<<===!=

目标值可以使用千字节 (k, ki)、兆字节 (m, mi) 或千兆字节 (g, gi) 的数量级。以 i 结尾的后缀使用适当的 2**n 版本,符合 IEC 标准

文件日期

使用 date() 方法按上次修改日期查找文件

1
$finder->date('since yesterday');

通过链式调用或传递数组来限制日期范围

1
2
3
4
$finder->date('>= 2018-01-01')->date('<= 2018-12-31');

// same as above
$finder->date(['>= 2018-01-01', '<= 2018-12-31']);

比较运算符可以是以下任何一种:>>=<<===。您还可以使用 sinceafter 作为 > 的别名,以及 untilbefore 作为 < 的别名。

目标值可以是 strtotime 支持的任何日期。

目录深度

默认情况下,Finder 递归遍历目录。使用 depth() 限制遍历深度

1
2
3
// this will only consider files/directories which are direct children
$finder->depth('== 0');
$finder->depth('< 3');

通过链式调用或传递数组来限制深度范围

1
2
3
4
$finder->depth('> 2')->depth('< 5');

// same as above
$finder->depth(['> 2', '< 5']);

自定义过滤

要使用您自己的策略过滤结果,请使用 filter()

1
2
3
4
5
6
7
8
$filter = function (\SplFileInfo $file)
{
    if (strlen($file) > 10) {
        return false;
    }
};

$finder->files()->filter($filter);

filter() 方法接受一个 Closure 作为参数。对于每个匹配的文件,都会使用该文件作为 SplFileInfo 实例来调用它。如果 Closure 返回 false,则该文件将从结果集中排除。

filter() 方法包含第二个可选参数来修剪目录。如果设置为 true,则此方法完全跳过排除的目录,而不是遍历整个文件/目录结构并在稍后排除它们。当使用闭包时,对于您要修剪的目录,返回 false

尽早修剪目录可以显著提高性能,具体取决于文件/目录层次结构的复杂性和排除目录的数量。

排序结果

按名称、扩展名、大小或类型(先目录,然后是文件)对结果进行排序

1
2
3
4
5
$finder->sortByName();
$finder->sortByCaseInsensitiveName();
$finder->sortByExtension();
$finder->sortBySize();
$finder->sortByType();

提示

默认情况下,sortByName() 方法使用 strcmp PHP 函数(例如,file1.txtfile10.txtfile2.txt)。传递 true 作为其参数以使用 PHP 的 自然排序 算法(例如,file1.txtfile2.txtfile10.txt)。

sortByCaseInsensitiveName() 方法使用不区分大小写的 strcasecmp PHP 函数。传递 true 作为其参数以使用 PHP 的不区分大小写的 自然排序 算法(即 strnatcasecmp PHP 函数)

按上次访问、更改或修改时间对文件和目录进行排序

1
2
3
4
5
$finder->sortByAccessedTime();

$finder->sortByChangedTime();

$finder->sortByModifiedTime();

您还可以使用 sort() 方法定义自己的排序算法

1
2
3
$finder->sort(function (\SplFileInfo $a, \SplFileInfo $b): int {
    return strcmp($a->getRealPath(), $b->getRealPath());
});

您可以使用 reverseSorting() 方法反转任何排序

1
2
// results will be sorted "Z to A" instead of the default "A to Z"
$finder->sortByName()->reverseSorting();

注意

请注意,sort* 方法需要获取所有匹配的元素才能完成其工作。对于大型迭代器,这很慢。

将结果转换为数组

Finder 实例是一个 IteratorAggregate PHP 类。因此,除了使用 foreach 迭代 Finder 结果之外,您还可以使用 iterator_to_array 函数将其转换为数组,或者使用 iterator_count 获取项目数。

如果您多次调用 in() 方法以搜索多个位置,请将 false 作为第二个参数传递给 iterator_to_array 以避免问题(为每个位置创建一个单独的迭代器,如果您不将 false 传递给 iterator_to_array,则会使用结果集的键,其中一些键可能会重复,并且其值会被覆盖)。

读取返回文件的内容

可以使用 getContents() 读取返回文件的内容

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Finder\Finder;

$finder = new Finder();
$finder->files()->in(__DIR__);

foreach ($finder as $file) {
    $contents = $file->getContents();

    // ...
}
本作品,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本