foreach 直接遍歷檔案、XML、資料庫結果、陣列,無需記憶不同 API。scandir 會)。RegexIterator、CallbackFilterIterator 組合式過濾。RecursiveDirectoryIterator + RecursiveIteratorIterator 兩行搞定樹狀遍歷。<?php
$dir = new DirectoryIterator(__DIR__ . '/../storage/logs');
foreach ($dir as $fileInfo) {
if ($fileInfo->isDot()) continue; // 跳過 . 和 ..
if ($fileInfo->isFile()) {
printf("%s %10d %s\n",
$fileInfo->getMTime()->format('Y-m-d H:i:s'),
$fileInfo->getSize(),
$fileInfo->getFilename()
);
}
}
| 方法 | 回傳 |
|---|---|
getFilename() | 檔名(不含路徑) |
getPathname() | 完整路徑 |
getBasename($suffix='') | 檔名(可去副檔名) |
isFile() / isDir() / isLink() | 型別判斷 |
isReadable() / isWritable() | 權限 |
getSize() | 位元組大小 |
getMTime() / getATime() / getCTime() | DateTime 物件 |
getPerms() | 權限位元遮罩 |
getOwner() / getGroup() | UID/GID |
<?php
$root = new RecursiveDirectoryIterator(
__DIR__ . '/../src',
FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS
);
// RecursiveIteratorIterator 扁平化遞歸遍歷
$iterator = new RecursiveIteratorIterator($root, RecursiveIteratorIterator::SELF_FIRST);
// 只取 .php 檔案
$phpFiles = new RegexIterator($iterator, '/\.php$/i');
foreach ($phpFiles as $file) {
echo $file->getPathname() . "\n";
}
SELF_FIRST:先輸出目錄本身,再遞歸子目錄(適合複製樹結構)。CHILD_FIRST:先遞歸子目錄,再輸出目錄本身(適合刪除樹、計算資料夾大小)。LEAVES_ONLY:只輸出葉子節點(檔案),忽略目錄節點。<?php
class TreeIterator extends RecursiveIteratorIterator {
public function __construct(RecursiveIterator $it) {
parent::__construct($it, self::SELF_FIRST);
}
public function current() {
$prefix = str_repeat('│ ', $this->getDepth() - 1);
$prefix .= $this->getDepth() ? '├── ' : '';
return $prefix . parent::current()->getBasename();
}
public function beginChildren() {
if ($this->getDepth() > 0) {
echo str_repeat('│ ', $this->getDepth() - 1) . "├── \n";
}
}
}
$dir = new RecursiveDirectoryIterator('/etc/nginx', FilesystemIterator::SKIP_DOTS);
foreach (new TreeIterator($dir) as $name) {
echo $name . "\n";
}
輸出範例:
nginx.conf
├── conf.d
│ ├── default.conf
│ └── ssl.conf
├── sites-available
│ └── example.com
└── sites-enabled
└── example.com -> ../sites-available/example.com
<?php
$dir = new RecursiveDirectoryIterator('/var/www', FilesystemIterator::SKIP_DOTS);
$rec = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::LEAVES_ONLY);
// 組合:只取 .php、大小 > 1KB、修改時間 7 天內
$filtered = new CallbackFilterIterator($rec, function ($file) {
return $file->isFile()
&& $file->getExtension() === 'php'
&& $file->getSize() > 1024
&& $file->getMTime() > new DateTime('-7 days');
});
foreach ($filtered as $file) {
echo $file->getPathname() . "\n";
}
| 方法 | 記憶體 | 速度 | 功能豐富度 |
|---|---|---|---|
scandir() | 全量陣列 | 快(單層) | 低 |
glob() | 全量陣列 | 中(模式匹配) | 中 |
DirectoryIterator | 惰性、常數 | 快 | 高(資訊豐富、可組合過濾) |
萬級檔案目錄建議用 SPL;百萬級需考慮 inotify 或資料庫索引。
SKIP_DOTS:導致無限遞歸或處理 . ..。try-catch 或用 FilesystemIterator::CURRENT_AS_FILEINFO 配合 @ 抑制。RecursiveDirectoryIterator::FOLLOW_SYMLINKS 預設關閉,開啟時需自行偵測循環。FilesystemIterator::UNIX_PATHS 統一輸出 /。原文《用PHP5的DirectoryIterators遞歸掃描目錄》重構,補充 SPL 完整體系、過濾器組合、ASCII 樹生成、效能對比與避雷指南。