如何扩展 PHP 文件扫描函数以支持多目录数组输入

2次阅读

如何扩展 PHP 文件扫描函数以支持多目录数组输入

本文介绍如何改造原有的单目录递归扫描函数,使其支持传入目录路径数组,一次性扫描多个根目录下的所有文件与子目录,并保持原有过滤、递归及类型控制逻辑。

在实际开发中,我们常需批量扫描多个不相关的目录(例如:/var/log/app1、/var/log/app2、/tmp/cache),而原 scanFiles() 函数仅接受单一字符串路径,无法直接满足该需求。最简洁且兼容性强的解决方案是:将参数 $Directory 改为可接受字符串或数组类型,并在函数入口做类型判断与统一处理——而非强制要求调用方始终传数组(兼顾向后兼容性)。

以下是优化后的完整实现,支持单路径(String)和多路径(Array)两种调用方式:

public static function scanFiles($directories, $recursive = true, $listDirs = false, $listFiles = true, $exclude = '') {     $arrayItems = [];      // 兼容单路径字符串:自动转为单元素数组     $dirs = is_string($directories) ? [$directories] : $directories;      foreach ($dirs as $directory) {         // 跳过空路径或非目录         if (!is_dir($directory) || !is_readable($directory)) {             continue;         }          $handle = opendir($directory);         if (!$handle) {             continue;         }          while (false !== ($file = readdir($handle))) {             // 默认排除项:. / .. / .git / .svn / .md / Thumbs.db / .DS_Store / .html             $skip = preg_match("/(^(([.]){1,2})$|(.(svn|git|md))|(Thumbs.db|.DS_STORE|.html))$/iu", $file);              // 自定义正则排除(如需)             $skipByExclude = false;             if ($exclude && !empty($exclude)) {                 $skipByExclude = (bool) preg_match($exclude, $file);             }              if (!$skip && !$skipByExclude) {                 $fullPath = $directory . DS . $file;                  if (is_dir($fullPath)) {                     if ($recursive) {                         // 递归扫描子目录(注意:此处仍传单路径字符串,递归调用会再次自动适配)                         $arrayItems = array_merge($arrayItems, self::scanFiles($fullPath, $recursive, $listDirs, $listFiles, $exclude));                     }                     if ($listDirs) {                         $arrayItems[] = $fullPath;                     }                 } else {                     if ($listFiles) {                         $arrayItems[] = $fullPath;                     }                 }             }         }         closedir($handle);     }      return $arrayItems; }

使用示例

// 扫描单个目录(完全兼容旧用法) $files = MyScanner::scanFiles('/var/www/html');  // 扫描多个目录 $paths = ['/var/log/nginx', '/var/log/php-fpm', '/tmp/uploads']; $allFiles = MyScanner::scanFiles($paths, true, false, true, '/^test_.*.log$/i');  // 同时列出目录和文件(含递归) $mixed = MyScanner::scanFiles(['/src', '/tests'], true, true, true);

⚠️ 注意事项与最佳实践

立即学习PHP免费学习笔记(深入)”;

  • 路径安全性:调用前建议对 $directories 中每个路径执行 realpath() 和白名单校验,防止路径遍历攻击(尤其在用户可控输入场景);
  • 性能提示:大量目录或深层嵌套时,opendir() + readdir() 效率优于 glob(),但需注意 open_basedir 限制;
  • 重复路径处理:函数本身不自动去重,若传入重复路径或存在符号链接交叉,需在调用层自行 array_unique($result, SORT_STRING);
  • DS 常量:确保已定义 DS(如 define(‘DS’, DIRECTORY_SEPARATOR);),以保证跨平台兼容性;
  • 错误抑制:当前代码跳过不可读目录,如需调试,可添加 error_log(“Cannot read directory: $directory”);。

该方案在零破坏原有接口的前提下,显著提升函数实用性,适用于日志聚合、资源打包、静态分析等典型多源扫描场景。

text=ZqhQzanResources