跳转到正文

文件遍历器

每种 Greph 模式(文本、AST、重写、索引文本、索引 AST、缓存 AST、rg 包装器、sg 包装器)都会先遍历文件树并生成一个 Greph\Walker\FileList。遍历器由所有模式共享,因此各处使用相同的标志和行为。

默认行为

遍历器开箱即用时会:

  • 递归进入所请求路径下的每个目录。
  • 在每一层遵循 .gitignore 和 .grephignore 规则,包括嵌套覆盖和否定规则。
  • 跳过 .git 目录。
  • 跳过隐藏文件和目录(名称以 . 开头的内容)。
  • 跳过二进制文件(通过文件前 512 个字节中的空字节检测)。
  • 跳过大于 10 MiB 的文件。
  • 不跟随符号链接。

覆盖标志

标志 效果
--no-ignore 停止遵循 .gitignore 和 .grephignore
--hidden 包含隐藏文件和目录
--type NAME 仅包含与指定类型别名匹配的文件
--type-not NAME 排除与指定类型别名匹配的文件
--glob GLOB 仅包含路径与 GLOB 匹配的文件。可重复指定。

这些选项也通过 Greph\Walker\WalkOptions 提供,以便以编程方式使用。

PHP
use Greph\Greph;
use Greph\Walker\FileTypeFilter;
use Greph\Walker\WalkOptions;

$files = Greph::walk('src', new WalkOptions(
    respectIgnore: false,
    includeHidden: true,
    fileTypeFilter: new FileTypeFilter(['php']),
    globPatterns: ['src/**/*.php'],
));

文件类型别名

Greph 附带一组小型命名文件类型。每个别名都对应一个扩展名列表:

别名 扩展名
css css、sass、scss
html htm、html、phtml
js cjs、js、mjs
json json
md markdown、md
php inc、php、php3、php4、php5、php7、php8、phpt、phtml
txt txt
ts ts、tsx
xml xml
yaml yaml、yml

完整列表见 Greph\Walker\FileTypeFilter::TYPE_MAP。

.gitignore 语义

Greph 使用纯 PHP 实现 gitignore 匹配。其语义与 git check-ignore 保持一致:

  • 模式相对于其所在目录进行求值。
  • 嵌套的 .gitignore 文件会覆盖父级规则。
  • 否定规则(!pattern)会重新包含先前排除的路径。
  • 末尾的 / 将模式限定为目录。
  • 开头的 / 将模式锚定到 .gitignore 所在目录。
  • ** 可匹配任意数量的路径段。

.grephignore 使用相同语法,并叠加在 .gitignore 之上。你可以在此添加不想纳入版本控制的工具专属排除规则。

实现位于 Greph\Walker\GitignoreFilter。

二进制文件检测

遍历器会检查每个文件前 512 个字节中是否含有空字节。如果存在空字节,该文件就会被视为二进制文件并跳过。这能捕获常见情况:仓库在已编入索引的目录中包含图像、归档或编译产物。

若仍要搜索二进制文件,请在相关的 Options 对象上设置 skipBinaryFiles: false。CLI 目前没有提供此标志;请使用 facade。

默认情况下不会跟随符号链接。设置 followSymlinks: true(或在 rg 包装器上传入 -L / --follow)即可跟随符号链接。遍历器会跟踪已访问的目录,因此符号链接循环不会导致无限递归。

最大文件大小

大于 maxFileSizeBytes(默认 10 MiB)的文件会被跳过。这可以避免意外将 1 GB 的日志文件读入内存,从而保护扫描过程。需要处理更大的输入时,可在 Options 对象上调整此限制。

为什么要自定义遍历器

Greph 可以依赖 glob() 或 RecursiveDirectoryIterator,但两者都没有实现 gitignore 语义,也都无法识别 .grephignore,并且都无法与工作池顺畅集成。只编写一次遍历器并在各模式间共享,也意味着每种模式应用的过滤规则完全相同,这对基于 oracle 的测试策略至关重要:要与 grep、ripgrep 和 ast-grep 比较,唯一办法是遍历这些工具所遍历的同一组文件。