首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 遞歸掃描目錄文件封裝類

     編輯:PHP 相關知識

本文提供一個輕量級的 PHP 目錄遞歸掃描函數,支持子目錄遍歷、文件過濾、自定義處理回調,可作為文件管理、批量處理的基礎工具。

核心函數實現

<?php
/**
 * 遞歸掃描目錄文件
 * @param string $dir 目錄路徑
 * @param callable|null $callback 處理回調 function($filePath, $isDir)
 * @param array $options 選項 ['skip_dots'=>true, 'follow_links'=>false]
 * @return array 文件路徑列表 (無回調時返回)
 */
function readFileFromDir($dir, $callback = null, $options = []) {
    $defaults = [
        'skip_dots' => true,
        'follow_links' => false,
        'max_depth' => -1, // -1 無限制
    ];
    $options = array_merge($defaults, $options);
    
    if (!is_dir($dir)) {
        return false;
    }
    
    $results = [];
    $handle = opendir($dir);
    
    if (!$handle) {
        return false;
    }
    
    // 使用棧模擬遞歸,避免深度過大導致棧溢出
    $stack = [[$dir, 0]];
    
    while (!empty($stack)) {
        [$currentDir, $depth] = array_pop($stack);
        
        if ($options['max_depth'] >= 0 && $depth > $options['max_depth']) {
            continue;
        }
        
        $dh = @opendir($currentDir);
        if (!$dh) continue;
        
        while (($file = readdir($dh)) !== false) {
            if ($options['skip_dots'] && ($file === '.' || $file === '..')) {
                continue;
            }
            
            $fullPath = $currentDir . DIRECTORY_SEPARATOR . $file;
            $isDir = is_dir($fullPath);
            
            // 符號鏈接處理
            if ($isDir && !$options['follow_links'] && is_link($fullPath)) {
                continue;
            }
            
            if ($callback) {
                $callback($fullPath, $isDir, $depth);
            } else {
                $results[] = $fullPath;
            }
            
            if ($isDir) {
                $stack[] = [$fullPath, $depth + 1];
            }
        }
        closedir($dh);
    }
    
    return $callback ? true : $results;
}
?>

使用示例

1. 基礎掃描獲取所有文件路徑

$files = readFileFromDir('/home/www/test');
print_r($files);

2. 回調模式處理(大目錄節省內存)

readFileFromDir('/var/log', function($path, $isDir, $depth) {
    $indent = str_repeat('  ', $depth);
    $type = $isDir ? '[DIR]' : '[FILE]';
    echo "{$indent}{$type} {$path}\n";
});

3. 過濾特定擴展名

4. 限制遞歸深度

// 僅掃描前 2 層
$files = readFileFromDir('/data', null, ['max_depth' => 2]);

進階封裝類版本

<?php
class DirectoryScanner {
    private $options = [
        'skip_dots' => true,
        'follow_links' => false,
        'max_depth' => -1,
    ];
    private $results = [];
    
    public function setOptions(array $options) {
        $this->options = array_merge($this->options, $options);
        return $this;
    }
    
    public function scan(string $dir, callable $callback = null): array {
        $this->results = [];
        $this->scanRecursive($dir, 0, $callback);
        return $callback ? [] : $this->results;
    }
    
    private function scanRecursive(string $dir, int $depth, callable $callback = null) {
        if ($this->options['max_depth'] >= 0 && $depth > $this->options['max_depth']) {
            return;
        }
        
        $handle = @opendir($dir);
        if (!$handle) return;
        
        while (($file = readdir($handle)) !== false) {
            if ($this->options['skip_dots'] && in_array($file, ['.', '..'])) continue;
            
            $fullPath = $dir . DIRECTORY_SEPARATOR . $file;
            $isDir = is_dir($fullPath);
            
            if ($isDir && !$this->options['follow_links'] && is_link($fullPath)) continue;
            
            if ($callback) {
                $callback($fullPath, $isDir, $depth);
            } else {
                $this->results[] = [
                    'path' => $fullPath,
                    'name' => $file,
                    'is_dir' => $isDir,
                    'depth' => $depth,
                    'size' => $isDir ? 0 : filesize($fullPath),
                    'mtime' => filemtime($fullPath),
                ];
            }
            
            if ($isDir) {
                $this->scanRecursive($fullPath, $depth + 1, $callback);
            }
        }
        closedir($handle);
    }
    
    public function getResults(): array {
        return $this->results;
    }
}

// 使用
$scanner = new DirectoryScanner();
$scanner->setOptions(['max_depth' => 3]);
$files = $scanner->scan('/var/www');
foreach ($files as $f) {
    echo "{$f['path']} ({$f['size']} bytes)\n";
}

替代方案對比

方法優點缺點適用場景
scandir()內置、簡單單級、一次性載入所有、大目錄佔內存小目錄快速列表
glob()支持模式匹配性能較差、大目錄慢特定模式匹配
RecursiveDirectoryIteratorSPL 標準、面向對象、支持過濾器語法較繁瑣現代項目標準選擇
自定義遞歸完全可控、內存友好(回調模式)需自行處理邊界情況特殊需求、舊版本兼容

現代標準寫法 (PHP 5.3+)

$iterator = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('/path', RecursiveDirectoryIterator::SKIP_DOTS)
);

foreach ($iterator as $file) {
    if ($file->isFile() && $file->getExtension() === 'php') {
        echo $file->getPathname() . "\n";
    }
}

// 限制深度
$iterator = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('/path'),
    RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
    if ($iterator->getDepth() > 3) continue;
    // ...
}

推薦使用 SPL 迭代器,代碼更規範、功能更強、性能更優。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved