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

PHP 檔案搜尋完整實作:遞迴遍歷、Glob 模式、內容搜尋與效能優化

     編輯:PHP 相關知識

前言

PHP 沒有內建「搜尋檔案」單一函式,需自行組合目錄遍歷、模式比對、內容掃描。本文提供四種實用搜尋函式:遞迴找檔名、Glob 模式比對、遞迴列出所有檔案、檔案內容全文搜尋,並討論大目錄效能優化、記憶體控制、權限處理等實務細節。

一、遞迴搜尋指定檔名(回傳第一個找到的完整路徑)

<?php
/**
 * 遞迴搜尋檔案
 * @param string $directory 起始目錄
 * @param string $filename  目標檔名(支援 basename 比對)
 * @param bool $recursive   是否遞迴子目錄
 * @return string|false 找到回傳完整路徑,否則 false
 */
function findFile($directory, $filename, $recursive = true) {
    $directory = rtrim($directory, '/\\') . DIRECTORY_SEPARATOR;
    $handle = @opendir($directory);
    if (!$handle) return false;

    while (($entry = readdir($handle)) !== false) {
        if ($entry === '.' || $entry === '..') continue;
        $path = $directory . $entry;

        if (is_dir($path)) {
            if ($recursive) {
                $found = findFile($path, $filename, true);
                if ($found !== false) {
                    closedir($handle);
                    return $found;
                }
            }
        } elseif ($entry === $filename) {
            closedir($handle);
            return $path;
        }
    }
    closedir($handle);
    return false;
}

// 使用
$path = findFile('/var/www/html', 'config.php');
if ($path) echo "找到:$path";
else echo "不存在";

優化:Generator 版(大目錄不佔記憶體)

function findFileGen($dir, $filename) {
    $dir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
    $it = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    foreach ($it as $file) {
        if ($file->isFile() && $file->getFilename() === $filename) {
            yield $file->getPathname();
        }
    }
}

// 取第一個
$first = findFileGen('/var/www', 'config.php')->current();

二、Glob 模式比對(支援萬用字元 * ? [...])

<?php
// 找所有 .php 檔
$phpFiles = glob('/var/www/html/**/*.php', GLOB_BRACE);
// 註:PHP 5.3+ 支援 ** 遞迴,否則需自行遞迴

// 更通用:自定遞迴 glob
function globRecursive($pattern, $flags = 0) {
    $files = glob($pattern, $flags);
    $dirs = glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT);
    foreach ($dirs as $dir) {
        $files = array_merge($files, globRecursive($dir . '/' . basename($pattern), $flags));
    }
    return $files;
}

$allPhp = globRecursive('/var/www/html/*.php');
print_r($allPhp);

Glob Flags 常用組合

Flag用途
GLOB_BRACE支援 {a,b,c} 多模式,如 *.{php,html,css}
GLOB_NOSORT不排序,較快
GLOB_ONLYDIR只回傳目錄
GLOB_NOESCAPE反斜線不轉義
GLOB_NOCHECK無符合時回傳模式本身

三、遞迴列出目錄所有檔案(Generator,記憶體恆定)

<?php
/**
 * 遞迴列出所有檔案路徑
 * @param string $dir
 * @return Generator
 */
function listAllFiles($dir) {
    $dir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
    $it = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    foreach ($it as $file) {
        if ($file->isFile()) {
            yield $file->getPathname();
        }
    }
}

// 使用:逐一處理,不佔記憶體
foreach (listAllFiles('/var/www/html') as $path) {
    // 處理單一檔案
    if (filesize($path) > 10 * 1024 * 1024) {
        echo "大檔:$path\n";
    }
}

四、檔案內容全文搜尋(支援編碼轉換、大檔分塊)

<?php
/**
 * 搜尋檔案內容包含關鍵字的檔案
 * @param string $dir 起始目錄
 * @param string $keyword 關鍵字
 * @param array $extensions 副檔名白名單,如 ['php','txt','js']
 * @param string $encoding 檔案編碼,預設 utf-8
 * @return Generator 產出 [path, lineNumber, matchedLine]
 */
function searchInFiles($dir, $keyword, $extensions = [], $encoding = 'utf-8') {
    $dir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
    $it = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );

    foreach ($it as $file) {
        if (!$file->isFile()) continue;
        $ext = strtolower($file->getExtension());
        if ($extensions && !in_array($ext, $extensions, true)) continue;

        $path = $file->getPathname();
        $handle = @fopen($path, 'r');
        if (!$handle) continue; // 權限不足等

        $lineNum = 0;
        while (($line = fgets($handle)) !== false) {
            $lineNum++;
            // 編碼轉 UTF-8 搜尋
            $searchLine = ($encoding !== 'utf-8') ? iconv($encoding, 'utf-8//IGNORE', $line) : $line;
            if (stripos($searchLine, $keyword) !== false) {
                yield [$path, $lineNum, rtrim($line)];
            }
        }
        fclose($handle);
    }
}

// 使用
foreach (searchInFiles('/var/www/html', 'TODO', ['php', 'js', 'css']) as [$path, $line, $content]) {
    echo "$path:$line: $content\n";
}

五、進階:排除目錄、限制深度、平行處理

排除目錄(如 vendor、.git、storage、node_modules)

function listFilesExclude($dir, $excludeDirs = ['vendor', '.git', 'node_modules', 'storage']) {
    $exclude = array_flip($excludeDirs);
    $it = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    foreach ($it as $file) {
        // 檢查路徑中是否包含排除目錄
        $parts = explode(DIRECTORY_SEPARATOR, $file->getPathname());
        if (array_intersect($parts, array_keys($exclude))) continue;

        if ($file->isFile()) yield $file->getPathname();
    }
}

限制遞迴深度

function listFilesDepth($dir, $maxDepth = 3) {
    $it = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    $it->setMaxDepth($maxDepth);
    foreach ($it as $file) {
        if ($file->isFile()) yield $file->getPathname();
    }
}

平行處理(需 pcntl 或外部程式)

PHP 單執行緒,大量檔案處理可用:

  • pcntl_fork 多進程(CLI 環境)。
  • exec("find ... -print0 | xargs -0 -P 4 php process.php") 外部平行。
  • Queue Worker(Laravel Queue、Symfony Messenger、自建 Redis Queue)分散處理。

六、效能與權限實務建議

問題解決
目錄極大(百萬檔)glob/scandir 記憶體爆掉用 RecursiveDirectoryIterator + Generator 逐一產出
權限不足導致警告/中斷@fopen、@opendir 抑制警告,失敗靜默跳過;CLI 以足夠權限用戶執行
符號連結導致無限遞迴RecursiveDirectoryIterator::FOLLOW_SYMLINKS 預設關閉;需小心開啟
網路掛載目錄(NFS/SMB)極慢避免在掛載點遞迴;改用本地索引(如 locate/mlocate)
內容搜尋二進制檔案產生雜訊副檔名白名單過濾;或檢測 is_binary(檢查 NUL 位元、高位元組比例)

七、整合到 Laravel / Symfony 專案

Laravel:

use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;

$files = File::allFiles(storage_path('app'));
$phpFiles = array_filter($files, fn($f) => $f->getExtension() === 'php');
// 或用 Finder
use Symfony\Component\Finder\Finder;
$finder = Finder::create()->files()->name('*.php')->in(storage_path('app'));

Symfony Finder 內建支援排除、深度、排序、內容搜尋,生產環境強烈建議直接用 Finder,勿重造輪子。

總結

  1. 找單一檔名:自訂遞迴函式或 RecursiveIteratorIterator Generator。
  2. 模式比對:glob() 配合 GLOB_BRACE,深層遞迴用自訂 globRecursive。
  3. 列出所有檔案:RecursiveDirectoryIterator + Generator,記憶體恆定。
  4. 內容搜尋:逐行 fgets + iconv 編碼統一 + Generator 產出匹配行。
  5. 生產專案:直接用 Symfony Finder 或 Laravel File,功能完整、效能優化、測試充分。

掌握迭代器與 Generator 模式,即能優雅處理任意規模的檔案搜尋需求。

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