PHP Conference Japan 2024

RecursiveDirectoryIterator 類別

(PHP 5, PHP 7, PHP 8)

簡介

RecursiveDirectoryIterator 類別提供了一個介面,用於遞迴迭代檔案系統目錄。

類別概要

class RecursiveDirectoryIterator extends FilesystemIterator implements RecursiveIterator {
/* 繼承的常數 */
/* 方法 */
公開 __construct(字串 $directory, 整數 $flags = FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO)
公開 getSubPath(): 字串
公開 hasChildren(布林值 $allowLinks = false): 布林值
公開 key(): 字串
公開 next():
公開 rewind():
/* 繼承方法 */
公開 SplFileInfo::getBasename(字串 $suffix = ""): 字串
公開 SplFileInfo::openFile(字串 $mode = "r", 布林值 $useIncludePath = false, ?資源 $context = null): SplFileObject
公開 SplFileInfo::setFileClass(字串 $class = SplFileObject::class):
公開 SplFileInfo::setInfoClass(字串 $class = SplFileInfo::class):
}

目錄

新增註解

使用者貢獻的註解 16 則註解

Thriault
14 年前
如果您想要遞迴取得專案資料夾中所有 *.php 檔案,可以使用以下程式碼

<?php

$Directory
= new RecursiveDirectoryIterator('path/to/project/');
$Iterator = new RecursiveIteratorIterator($Directory);
$Regex = new RegexIterator($Iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);

?>

$Regex 將包含每個 PHP 檔案的單一索引陣列。
sun
10 年前
由於我持續在網路上看到一些實作無意中遇到了這個陷阱 — 請小心

RecursiveDirectoryIterator 會無限制地遞迴進入整個檔案系統樹。

除非您有意要無限制地遞迴,否則不要執行以下操作

<?php
$directory
= new \RecursiveDirectoryIterator($path);
$iterator = new \RecursiveIteratorIterator($directory);
$files = array();
foreach (
$iterator as $info) {
if (...
自訂條件...) {
$files[] = $info->getPathname();
}
}
?>

1. RecursiveDirectoryIterator 只是一個 RecursiveIterator,它會遞迴進入其子項目,直到找不到更多子項目為止。

2. RecursiveIteratorIterator 的實例化會導致 RecursiveDirectoryIterator *立即* 無限遞迴進入整個檔案系統樹(從給定的基底路徑開始)。

3. 不必要的檔案系統遞迴很慢。在 90% 的情況下,這不是您想要的。

請記住這個簡單的經驗法則

→ RecursiveDirectoryIterator 必須經過篩選,或者您有充分的理由說明為什麼不應該篩選。

在 PHP 5.4 之前的版本,需要實現以下程式碼 - 將您的自訂條件移到一個適當的過濾器中

<?php
$directory
= new \RecursiveDirectoryIterator($path, \FilesystemIterator::FOLLOW_SYMLINKS);
$filter = new MyRecursiveFilterIterator($directory);
$iterator = new \RecursiveIteratorIterator($filter);
$files = array();
foreach (
$iterator as $info) {
$files[] = $info->getPathname();
}

class
MyRecursiveFilterIterator extends \RecursiveFilterIterator {

public function
accept() {
$filename = $this->current()->getFilename();
// 略過隱藏的檔案和目錄。
if ($filename[0] === '.') {
return
FALSE;
}
if (
$this->isDir()) {
// 只遞迴進入指定的子目錄。
return $filename === 'wanted_dirname';
}
else {
// 只處理需要的檔案。
return strpos($filename, 'wanted_filename') === 0;
}
}

}
?>

在 PHP 5.4 以上的版本,PHP 核心解決了必須建立一個全新類別的這個略為繁瑣的問題,您可以改用新的 RecursiveCallbackFilterIterator。

<?php
$directory
= new \RecursiveDirectoryIterator($path, \FilesystemIterator::FOLLOW_SYMLINKS);
$filter = new \RecursiveCallbackFilterIterator($directory, function ($current, $key, $iterator) {
// 跳過隱藏的檔案和目錄。
if ($current->getFilename()[0] === '.') {
return
FALSE;
}
if (
$current->isDir()) {
// 只遞迴進入指定的子目錄。
return $current->getFilename() === 'wanted_dirname';
}
else {
// 只處理感興趣的檔案。
return strpos($current->getFilename(), 'wanted_filename') === 0;
}
});
$iterator = new \RecursiveIteratorIterator($filter);
$files = array();
foreach (
$iterator as $info) {
$files[] = $info->getPathname();
}
?>

玩得開心!
alvaro at demogracia dot com
16 年前
使用範例

<?php

$path
= realpath('/etc');

$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach(
$objects as $name => $object){
echo
"$name\n";
}

?>

這會印出 $path 下的所有檔案和目錄的列表(如果 $path 本身存在,也包含 $path)。如果要省略目錄,請移除 RecursiveIteratorIterator::SELF_FIRST 部分。
TDP
7 年前
Windows 和 Linux 在檔案順序上有所不同。

<?php
$it
= new RecursiveIteratorIterator(
new
RecursiveDirectoryIterator( 'path/to/dir' )
//, 旗標無關緊要
);
...
?>

在 Windows 上,您會得到按名稱排序的檔案。在 Linux 上,它們未排序。
catinahat at cool dot fr dot nf
11 年前
如果您需要將巢狀目錄樹轉換為多維陣列,請使用以下程式碼

<?php
$ritit
= new RecursiveIteratorIterator(new RecursiveDirectoryIterator($startpath), RecursiveIteratorIterator::CHILD_FIRST);
$r = array();
foreach (
$ritit as $splFileInfo) {
$path = $splFileInfo->isDir()
? array(
$splFileInfo->getFilename() => array())
: array(
$splFileInfo->getFilename());

for (
$depth = $ritit->getDepth() - 1; $depth >= 0; $depth--) {
$path = array($ritit->getSubIterator($depth)->current()->getFilename() => $path);
}
$r = array_merge_recursive($r, $path);
}

print_r($r);
?>
antennen
13 年前
如果您將 RecursiveDirectoryIterator 與 RecursiveIteratorIterator 搭配使用,並遇到 UnexpectedValueException,您可以使用這個小技巧來忽略這些目錄,例如 Linux 上的 lost+found。

<?php
class IgnorantRecursiveDirectoryIterator extends RecursiveDirectoryIterator {
function
getChildren() {
try {
return new
IgnorantRecursiveDirectoryIterator($this->getPathname());
} catch(
UnexpectedValueException $e) {
return new
RecursiveArrayIterator(array());
}
}
}
?>

用法與一般的 RecursiveDirectoryIterator 相同。
megar
15 年前
使用範例
要查看所有檔案並計算空間使用量

<?php
$ite
=new RecursiveDirectoryIterator("/path/");

$bytestotal=0;
$nbfiles=0;
foreach (new
RecursiveIteratorIterator($ite) as $filename=>$cur) {
$filesize=$cur->getSize();
$bytestotal+=$filesize;
$nbfiles++;
echo
"$filename => $filesize\n";
}

$bytestotal=number_format($bytestotal);
echo
"Total: $nbfiles files, $bytestotal bytes\n";
?>
alexandrebr at gmail dot com
8 年前
我嘗試使用 RecursiveDirectoryIterator 來傾印一個大型目錄 (~400,000 個檔案) 中的所有檔案 (及其屬性,例如大小/是否為連結/是否為目錄/修改時間/權限/所有者/群組),並過濾一些特定的目標檔案/資料夾。

使用 RecursiveDirectoryIterator 和 SplFileInfo,傾印大約需要 50 秒才能執行,但它可以正常運作。

然而,為了提高效能,我決定製作另一個相同腳本的版本,僅使用直接的檔案函式,例如 "readdir"、"filesize"、"filemtime" 等,並自行添加遞迴 (如果 (is_dir($path)) 則執行 doRecursivity($path);

執行後,腳本從約 50 秒縮短到僅約 20 秒即可完成(在 Linux CentOS 7、SSD 300IPs 上)。

奇怪的是,在 Windows 7、Sata3 上(使用完全相同的檔案 [鏡像]),時間從約 63 秒縮短到約 57 秒。

我相信這個負載是由於 SPL 的物件導向方法,它會執行許多不必要的額外程式碼來以更高的可靠性執行相同的任務,而直接的檔案函式更像是對應 C 函式的別名,因此速度更快。

因此,如果您正在處理大量檔案,使用 RecursiveDirectoryIterator 可能不是一個好方法。
dev_zakaria at outlook dot com
5 年前
如果您想顯示所有目錄中的所有檔案,您必須遵循以下步驟 -

$dir = new RecursiveDirectoryIterator(getcwd());
$files = new RecursiveIteratorIterator($dir);

foreach($files as $file){
echo $file->getFileName();
echo PHP_EOL; // 換行
}

現在,如果您想顯示完整路徑,請遵循以下步驟 -

$dir = new RecursiveDirectoryIterator(getcwd());
$files = new RecursiveIteratorIterator($dir);

foreach($files as $file){
echo $file->getPath().$file->getFileName();
echo PHP_EOL;
}

如果您想略過點,您需要使用以下內容更改第一行 -
$dir = new RecursiveDirectoryIterator(getcwd(), RecursiveDirectoryIterator::SKIP_DOTS);
Josh Heidenreich
12 年前
返回的物件是 SplFileInfo 物件的迭代器。
flobee
9 年前
在這個文件中,我看到了隱藏檔案的方法(也適用於 opendir() 或 readdir() .... 所有這些都應該被提及
<?php
// 在大多數作業系統(Win, *nix, OSX..)上並非隱藏
if ($file == '.' || $file == '..') {
// "." 代表目前目錄資訊,
// ".." 代表上一層目錄資訊,
continue;
?>
或者
<?php
if $name[0] === '.' // 這樣不行喔
?>
試想
如果影片標題是「... and then came Polly.avi」,那該怎麼辦?

Windows 處理隱藏檔案的方式與 Unix 系統不同。

對於 Unix 系統,應該可以這樣做:
<?php
if (preg_match('/^(\.\w+|\.$|\.\.$)/i', $location)) {
/* 是隱藏檔案:
.
..
.dir
.file
*/
}
// 這樣就沒問題: "..some thing", "... some thing"
?>

我知道你用 (if $name[0] === '.' ) 是因為速度比較快。但這並不正確,總有一天你會像我今天一樣錯過一些東西 :-)
divinity76+spam at gmail dot com
2 年前
如果你只是想要一個純粹的陣列,遞迴地取得所有檔案(而不是目錄),可以試試看:

<?php

/**
* 傳回一個純字串陣列,包含目錄及子目錄中所有檔案的路徑,
* 但不包含目錄本身。(也就是說,如果目錄是空的,它根本不會被包含)
*
* @param string $dir
* @param bool $realpath
* @throws UnexpectedValueException 如果 $dir 無法讀取/不存在
* @return string[] 檔案
*/
function get_file_list_recursively(string $dir, bool $realpath = false): array
{
$files = array();
$files = [];
foreach ((new
RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS))) as $file) {
/** @var SplFileInfo $file */
if ($realpath) {
$files[] = $file->getRealPath();
} else {
$files[] = $file->getPathname();
}
}
return
$files;
}
?>

範例回傳值
<?php
array (
0 => '/home/hans/scraping/1650518081RESULTS.txt',
1 => '/home/hans/scraping/1650518121RESULTS.txt',
2 => '/home/hans/scraping/1650518679RESULTS.txt',
3 => '/home/hans/scraping/1650518780RESULTS.txt',
4 => '/home/hans/scraping/1650522198RESULTS.txt',
5 => '/home/hans/scraping/1650522927RESULTS.txt',
6 => '/home/hans/scraping/1650525391RESULTS.txt',
7 => '/home/hans/scraping/check_cache.php',
8 => '/home/hans/scraping/foo/bar.txt',
9 => '/home/hans/scraping/foobar.txt',
10 => '/home/hans/scraping/GoodProxyDaemon.php',
);
?>

(給編輯的說明:如果您能找到更好的範例回傳值,請自行覆蓋上面的內容)
dxvargas
5 年前
當使用 FilesystemIterator::FOLLOW_SYMLINKS 選項時,如果存在指向上一層目錄的符號連結,就會出現迴圈,導致目錄重複。
例如(有一個 -> ../ )

/c
/c/..
/c/a
/c/a/c
/c/a/c/..
/c/a/c/a
/c/a/c/a/c
... (最多 40 個 /c/a )
/c/a/c/a/..
/c/a/c/a/.
/c/a/c/.
/c/a/..
/c/a/.
/c/.
/..
/.

顯然有一個限制可以避免無限迴圈。我不知道這方面的文件。

如果能有一個選項允許追蹤符號連結而不產生迴圈,那就太好了。
Edward Rudd
10 年前
(與關於 getChildren() 中異常的貼文相關。

除了建立子類別之外,您也可以直接對 RecursiveIteratorIterator 使用 CATCH_GET_CHILD 旗標

new RecursiveIteratorIterator($diriter, RecursiveIteratorIterator::CATCH_GET_CHILD);
flaurora_sonora
5 年前
別浪費時間在這上面了。我在下面提供了一個函式,可以更簡單地完成同樣的事情。如果您想在迭代時使用正規表達式,可以將正規表達式作為參數傳遞給遞迴函式。

<?php

函式 recursive_read($directory, $entries_array = array()) {
if(
is_dir($directory)) {
$handle = opendir($directory);
while(
FALSE !== ($entry = readdir($handle))) {
if(
$entry == '.' || $entry == '..') {
continue;
}
$Entry = $directory . DS . $entry;
if(
is_dir($Entry)) {
$entries_array = recursive_read($Entry, $entries_array);
} else {
$entries_array[] = $Entry;
}
}
closedir($handle);
}
return
$entries_array;
}

?>
dblanchard1 at bbox dot fr
11 年前
如果您想遞迴地將所有檔案從來源目錄複製到某個目標目錄

$directory = new RecursiveDirectoryIterator("./source_path/");

foreach (new RecursiveIteratorIterator($directory) as $filename=>$current) {

$src = $current->getPathName();
$dest = "./destination_path/" . $current->getFileName();

echo "複製 " . $src . " => " . $dest . "\n";

copy($src, $dest);
}

我希望這可以幫助到其他人,因為當我尋找這個解決方案時,我不得不修改另一個範例才能得到它。
To Top