PHP 檔案系統完全指南

liuqing_hu發表於2018-07-20

本文首發於個人部落格 PHP 檔案系統完全指南,轉載請註明出處。

今天我們將開啟一個新的探索旅程,深入到 PHP 檔案系統中,系統的學習和掌握 PHP 檔案系統的基本使用。

相信大家在日常研發過程中,難免需要和各種檔案糾纏不清。比如,開啟 .env 檔案並從中讀取配置資訊、把專案中的錯誤資訊寫入到日誌檔案中或者獲取圖片的建立時間等等。在處理這些功能時,我們都需要使用到 PHP 檔案系統介面。

下面是本文所涉主題的提綱:

  • 一 什麼是檔案系統
  • 二 深入 PHP 檔案系統
  • 三 物件導向的目錄遍歷
  • 四 PHP 檔案系統思維導圖

本文較長,耗時約 20 分鐘,請做好戰鬥準備!

一 什麼是檔案系統

開始之前,我們首先需要釐清我們所研究的問題領域,理解什麼是檔案系統,還有我們所研究的物件。

在計算機中,檔案系統(file system or filesystem)用於管理資料如何儲存和如何被獲取的。 - 維基百科

簡單來說,就是我們應該如何管理我們的目錄(資料夾)和檔案。通常,我們將具有相似屬性的檔案,儲存到同一個目錄中以便後續查詢,這個常見的操作就會涉及到目錄和檔案。

對於軟體工程師來講,一個非常典型的使用場景,就是在開發 MVC 專案時,將控制器、檢視和模型等模組的檔案,儲存到不同的目錄結構中方便管理。

無論如何,我們依據不同特性劃分檔案和目錄都是為了解決檔案儲存和查詢的問題。

有了這些認知後,應該自然而然的想到我們當前研究的 PHP 檔案系統(或者說檔案系統)其所研究的物件,簡單概括起來就是:

  1. 目錄(資料夾)
  2. 檔案

也就是說,本文我們所講解的 PHP 檔案系統函式處理,基本都是圍繞目錄和檔案展開的。

二 深入 PHP 檔案系統

在 PHP 檔案系統中內建提供了超過 80 個可用的 檔案系統函式。由於數量繁多功能強大,自然本文無法將對所有的系統函式逐一講解。一來,時間過於倉促;再者,短時間內我們也沒有那麼多的精力將它們全部掌握。

儘管如此,大家也不必氣餒,本文會將有限的時間和精力,來研究以下幾個在檔案處理時的常見話題:

  • 檔案的後設資料應該如何獲取
  • 檔案的 MIME 型別如何獲取
  • 檔案和目錄的操作處理
  • 檔案和目錄的許可權管理

另外,補充說明一點,PHP 標準函式庫不僅為我們提供了程式導向的檔案系統處理函式。同時,還封裝了常用目錄及檔案操作的物件導向介面和迭代器介面方便大家使用:

2.1 檔案系統的後設資料

2.1.1 什麼是後設資料

後設資料(meta data):通俗一點講就是「資料的資料」。拿一個 php 檔案來說它的後設資料可以是 建立時間檔名檔案大小檔案所有許可權 等,這類能夠表明該檔案基本特徵的資料就是「後設資料(meta data)」了。

2.1.2 常用後設資料獲取

在這一節,我們將學習一些經常需要獲取的檔案後設資料函式,包括:

  1. 獲取檔案的最後修改時間
  2. 獲取檔案的上次訪問時間
  3. 獲取檔案的路徑資訊
  4. 獲取檔案的絕對路徑
  5. 獲取檔案型別
  6. 獲取檔案大小
  7. 獲取檔案許可權
  8. 獲取檔案所屬使用者及使用者組

話不多說,擼起袖子開幹吧!

    1. 獲取檔案的最後修改時間

要獲取檔案的上次被修改時間戳,我們可以使用函式 filemtime($filename)SplFileInfo::getMTime() 方法。

注意 SplFileInfo 類例項化時接收 $filename 檔案路徑作為引數,後續沒有特別說明預設我們已經獲取到了 SplFileInfo 例項才能進行 getMTime() 等類似處理。

// 檔案路徑請求改成你自己的檔案路徑
$filename = "f://filesystem/test.txt";

// 程式導向: 獲取檔案時間
$modifyTimestamp = filemtime($filename);

// 物件導向
$file = new SplFileInfo($filename);
$modifyTimestamp = $file->getMTime();
  • 2 獲取檔案的上次訪問時間

可以使用函式 fileatile($filename)SplFileInfo::getATime() 方法,來獲取檔案的最後被訪問時間戳。

// 檔案路徑請求改成你自己的檔案路徑
$filename = "f://filesystem/test.txt";

// 程式導向: 獲取檔案時間
$accessTimestamp = fileatime($filename);

// 物件導向
$file = new SplFileInfo($filename);
$accessTimestamp = $file->getATime();

除了 filemtilefileatime 之外,還有 filectime 來獲取檔案的 inode 修改時間(可認為是建立時間)。

有關時間的函式常用的就這些,為了方便記住,我們來看看它們是如何命名的:

  • 2.1 程式導向 file 字首,物件導向 get 字首
  • 2.2 a: access(訪問);m:modify(修改);c:create(建立)
  • 2.3 time 字尾
  • 2.4 fileatime,SplFileInfo::getATime;filemtime,SplFileInfo::getMTime;filectime,SplFileInfo::getCTime。

是不是很簡單呢!

注意,使用 filectime 時,對於 Windows 系統會獲取建立時間,但對於類 Unix 系統是修改時間,因為在類 Unix 系統中多數檔案系統並沒有建立時間的概念。具體說明可以看 PHP: how can I get file creation date?

  • 3 獲取檔案的路徑資訊

除了時間這些後設資料,另一個經常遇到的情況是獲取檔案的路徑資訊,包括:

  • 3.1 目錄資訊

獲取目錄資訊我們可以使用 pathinfo(\$filename, PATHINFO_DIRNAME)dirname(\$filename)SplFileInfo::getPath()

比如下面給出的檔案:

$filename = 'F:\Program Files\SSH Communications Security\SSH Secure Shell\Output.txt';

將會獲取到 F:\Program Files\SSH Communications Security\SSH Secure Shell 這部分目錄資訊。

  • 3.2 檔名資訊

這裡我們所有的檔名指的是不帶副檔名字尾的檔名稱,比如需要獲取 your_path/filename.txt 中的 filename 部分。

需要取得檔名資訊,我們可以使用 pathinfo(\$filename, PATHINFO_FILENAME)basename(\$filename, \$suffix)SplFileInfo::getBasename(\$suffix) 獲取。

這裡給出的 $suffix 指不獲取 $suffix 副檔名部分(比如不獲取 $suffix = '.txt')。

請看下面的示例:

$filename = 'F:\Program Files\SSH Communications Security\SSH Secure Shell\Output.txt';

將會獲取到 Output 這部分檔名資訊。

  • 3.3 副檔名資訊

副檔名我們可以使用 pathinfo(\$filename, PATHINFO_EXTENSION)SplFileInfo::getExtension() 方法拿到。

基於前面的瞭解,我們可以獲取到 txt 這部分擴充套件資訊,這裡不再贅述。

  • 3.4 basename(檔名 + 副檔名)資訊

basename 指的是 檔名 + 副檔名 內容資訊,可以使用 pathinfo(\$filename, PATHINFO_BASENAME)basename(\$filename)SplFileInfo::getBasename()SplFileInfo::getFilename() 方法拿到。

雖然這裡我們列出了很多的函式,但是基本上還是比較容易理解的,需要注意的是:

  • pathinfo 可以獲取所有檔案相關的路徑資訊,如果指定第二個引數選項將僅獲取該部分的資訊
  • 檔名和 basename 不是特別容易理解,你可以使用完全相同的方法或函式 basenameSplFileInfo::getBasename() 獲取他們,區別在於是否摘除指定的 $suffix 字尾。

  • 3.5 示例
<?php

$filename = 'F:\Program Files\SSH Communications Security\SSH Secure Shell\Output.txt';

$file = new SplFileInfo($filename);

// 目錄路徑
$directory1 = pathinfo($filename, PATHINFO_DIRNAME);
$directory2 = dirname($filename);
$directory3 = $file->getPath();

echo '--- directory begin: ---' . PHP_EOL;
echo $directory1 . PHP_EOL, $directory2 . PHP_EOL, $directory3 . PHP_EOL;

// 檔名
$suffix = '.txt';
$filename1 = pathinfo($filename, PATHINFO_FILENAME);
$filename2 = basename($filename, $suffix);
$filename3 = $file->getBasename($suffix);

echo '--- filename begin: ---' . PHP_EOL;
echo $filename1 . PHP_EOL, $filename2 . PHP_EOL, $filename3 . PHP_EOL;

// 副檔名
$extension1 = pathinfo($filename, PATHINFO_EXTENSION);
$extension2 = $file->getExtension();

echo '--- extension begin: ---' . PHP_EOL;
echo $extension1 . PHP_EOL, $extension2 . PHP_EOL;

// basename = 檔名 + 副檔名
$basename1 = pathinfo($filename, PATHINFO_BASENAME);
$basename2 = basename($filename);
$basename3 = $file->getBasename();
$basename4 = $file->getFilename();

echo '--- basename begin: ---' . PHP_EOL;
echo $basename1 . PHP_EOL, $basename2 . PHP_EOL, $basename3 . PHP_EOL, $basename4 . PHP_EOL;

它們的執行結果如下:

--- directory begin: ---
F:\Program Files\SSH Communications Security\SSH Secure Shell
F:\Program Files\SSH Communications Security\SSH Secure Shell
F:\Program Files\SSH Communications Security\SSH Secure Shell
--- filename begin: ---
Output
Output
Output
--- extension begin: ---
txt
txt
--- basename begin: ---
Output.txt
Output.txt
Output.txt
Output.txt
  • 3.6 檔案路徑資訊關係圖

檔案路徑資訊關係

另外需要注意的一點是在使用 SplFileInfo 獲取 basename 時,getBasename() 和 getFilename() 返回基本一致,但是在處理根目錄下的檔名獲取時表現稍有不同。
這裡可以到官方文件中使用者 提交的反饋 去詳細瞭解一下。

  • 4 獲取檔案的絕對路徑

絕對路徑由 realpath($path)SplFileInfo::getRealpath() 獲取。

  • 5 獲取檔案型別

可以使用 filetype($filename)SplFileInfo::getType() 來獲取檔案的型別。

返回值範圍:

  • dir
  • file
  • char
  • fifo
  • block
  • link
  • unknown

可以檢視 Linux 檔案型別與副檔名 相關檔案型別,這裡我們重點關注下 dir 目錄和 file 普通檔案型別即可。

  • 6 獲取檔案大小

可以使用 filesize(\$filename)SplFileInfo::getSize() 來獲取檔案的大小,不再贅述。

  • 7 獲取檔案許可權

可以使用 fileperms(\$filename)SplFileInfo::getPerms() 來獲取到檔案的所屬許可權。

值得注意的是它們的返回值是十進位制表示的許可權,如果需要獲取類似 0655 八進位制許可權表示法,我們需要對返回值進行處處理才行:

// @see  http://php.net/manual/zh/function.fileperms.php#refsect1-function.fileperms-examples
$permissions = substr(sprintf("%o", fileperms($filename)), -4);

你可以通過 PHP: fileperms() values and convert these 瞭解更多關於 PHP 獲取檔案許可權轉換的更多細節。

基本上學習完這些檔案後設資料資訊獲取方法,差不多可以應對日常開發過程中的多數應用場景,儘管如此,還是建議仔細去閱讀官方 檔案系統函式,那裡才是知識的源泉。

掌握檔案的後設資料,對我們瞭解檔案的特性大有裨益,就好比兩個人談戀愛,懂得彼此才是最好的狀態。

2.2 檔案系統操作

可以說我們日常在處理檔案的過程中,更多的是在操作檔案或者目錄(資料夾),本節我們將學習檔案系統操作相關知識。

依據檔案型別的不同我們可以簡單的將操作分為:

  • 對目錄(dir)的操作
  • 和對普通檔案(file)的操作

2.2.1 目錄操作使用場景

在處理目錄時我們一般涉及如下處理:

  1. 建立目錄
  2. 刪除目錄
  3. 開啟目錄
  4. 讀取目錄
  5. 關閉目錄控制程式碼
  • 場景一

我們有一套 CMS 管理系統支援檔案上傳處理,當目錄不存在時依據檔案上傳時間,動態的建立檔案儲存目錄,比如,我們依據 年/月/日(2018/01/01) 格式建立目錄。這裡就涉及到 目錄建立 的處理。

  • 場景二

當然,檔案上傳完成了還不夠,我們還需要讀取各個目錄下的所有檔案。這裡涉及 開啟目錄讀取目錄 以及讀取完成後 關閉目錄控制程式碼

有了相關概念和思路後,我們具體看看究竟 PHP 檔案系統給我們提供了哪些方便處理目錄的函式呢?

2.2.1.1 建立目錄

在 PHP 檔案系統擴充套件中同樣給我們提供了處理 目錄結構的系統函式

其中建立一個新目錄需要使用 mkdir($pathname [, $mode = 0777, $recursive = false]) 函式。

  1. $pathname 引數為待建立目錄的路徑
  2. $mode 為建立目錄時的訪問許可權,0777 意味著獲取最大訪問許可權
  3. $recursive 用於標識是否遞迴建立目錄,預設 false 不會遞迴建立

請看一個示例:

$pathname = "/path/to/your/upload/file/2018/01/01";
$created = mkdir($pathname);

建立目錄是不是特別的簡單呢?

但是等等,我們在類 Unix 系統中滿心歡喜的使用 mkdir 並採用 $mode=0777 許可權來建立一個全新的目錄,但為什麼當我們進入到目錄中看到的目錄的許可權卻是 0755 呢?

umask 掩碼

這裡涉及到 umask 掩碼的問題!

重點: 原來我們在類 Unix 系統中建立新目錄是給出的許可權會預設減去當前系統的 umask 值,才是實際建立目錄時的所屬許可權。

什麼意思呢?

比如:

// 我們期望建立的檔案許可權
$mode = 0777;

// 當前系統中 umask 值
$umask = 0022;// 可以由 umask 命令檢視當前系統 umask 值,預設是 0022

// 實際建立的檔案許可權
  0777
- 0022
------
= 0755

現在我們來對之前的例項稍作修改,看看 PHP 如何建立目錄時得到希望的系統許可權吧:

$pathname = "/path/to/your/upload/file/2018/01/01";

// 將系統 umask 設定為 0,並取得當前 umask 值(比如預設 0022)
$umask = umask(0);
$created = mkdir($pathname, $mode = 0777);
// 將系統 umask 設定回原值
umask($umask);

有關 umask 函式說明可以檢視官方手冊。另外可以檢視 Why can't PHP create a directory with 777 permissions? 這個問答瞭解更多細節。

2.2.1.2 目錄遍歷

程式導向的目錄遍歷提供兩種解決方案:

  1. 通過 opendirreaddirclosedir 來遍歷目錄;
  2. 另一種是直接使用 scandir 遍歷指定路徑中的檔案和目錄。

目錄遍歷示例一,出自 官方文件

<?php

$dir = "/etc/php5/";

// Open a known directory, and proceed to read its contents
if (is_dir($dir)) {
    if ($dh = opendir($dir)) {
        while (($file = readdir($dh)) !== false) {
            echo "filename: $file : filetype: " . filetype($dir . $file) . "\n";
        }
        closedir($dh);
    }
}

// 輸出結構類似於:
// filename: . : filetype: dir
// filename: .. : filetype: dir
// filename: apache : filetype: dir
// filename: cgi : filetype: dir
// filename: cli : filetype: dir
?>

目錄遍歷示例二,出自 官方文件

<?php
$dir    = '/tmp';
$files1 = scandir($dir);

print_r($files1);

// 輸出結構類似於:
// Array
// (
//     [0] => .
//     [1] => ..
//     [2] => bar.php
//     [3] => foo.txt
//     [4] => somedir
// )

目錄的操作處理大致就是在處理這兩類問題,相比於普通檔案的處理來講簡單很多,下一節我們會學習有關普通檔案的處理,請大家做好戰鬥準備。

2.2.2 檔案操作使用場景

可以說我們在處理檔案系統時,絕大多數都是在處理一個普通檔案,那麼我們在操作檔案時,我們究竟在做什麼呢?

你可能已經想到了,沒錯我們多數時候就是在處理如下檔案問題:

  • 建立一個新的空檔案
  • 開啟一個檔案控制程式碼,以供後續讀取或寫入
  • 將檔案中的內容覆蓋掉(覆蓋寫入),或者在檔案末尾寫入新的內容(追加寫入)
  • 讀取檔案的內容
  • 刪除檔案
  • 複製檔案
  • 關閉檔案控制程式碼

檔案的讀取和寫入相對會複雜一些,所以這兩部分的內容會在稍後詳細講解。先讓我們看看其它幾個常見檔案處理。

2.2.2.1 建立空檔案

建立空檔案有兩種方式:
一是:以寫入(w)模式使用 fopen($filename, $mode = 'wb') 開啟一個檔案,當檔案不存在時則會建立一個新檔案;
二是:使用 touch 函式建立一個新檔案。

這兩個函式同其它檔案系統函式使用大致相同,感興趣的朋友可以閱讀手冊,這裡不作展開。

2.2.2.2 刪除檔案

刪除檔案由 unlink($filename) 函式完成。

2.2.2.3 複製檔案

複製檔案由 copy($source, $dest) 函式完成,會將 $source 檔案拷貝到 $dest 檔案中。

如果需要移動檔案(重新命名)可以使用 rename($oldname, $newname) 完成這個處理。

以上都是相對簡單的檔案處理函式就不一一舉例說明了。

接下來學習如何讀取檔案中的內容。依據二八原則,可以說我們百分之八十的時間都在處理檔案寫入和讀取的處理,所以我們有必要理清如何對檔案進行讀取和寫入。

2.2.2.4 讀取檔案

讀取檔案的標準流程是:

    1. 開啟一個檔案控制程式碼;
    1. 使用檔案讀取函式讀取檔案;
    1. 判斷是否到檔案結尾,到結尾則結束讀取,否則回到操作 2;
    1. 讀取完成關閉控制程式碼;

開始之前我們需要準備一個有資料的檔案,比如 F:\php_workspace\php-code-kata\read.txt,在看一個簡單的檔案讀取示例:

<?php

// 這裡為了貼合讀取檔案的標準流程,使用 do{} while 語句,你也可以修改成 while 語句。

$filename = "F:\\php_workspace\\php-code-kata\\read.txt";

// 1. 開啟一個檔案控制程式碼;
$handle = fopen($filename, $mode = 'rb');

do {
    // 2. 使用檔案讀取函式讀取檔案;
    $content = fgetc($handle);
    echo $content;

    // 3. 判斷是否到檔案結尾,到結尾則結束讀取,否則回到操作 2;
} while (!feof($handle));

// 4. 讀取完成關閉控制程式碼;
fclose($handle);

// 讀取顯示大致類似:
// hello world!

現在,我們來詳細講解一下上述程式碼做了什麼處理吧:

  • 使用 fopen($filename, $mode) 開啟一個檔案或 URL 控制程式碼,供後續檔案系統函式使用;
  • 使用 fgetc($handle) 函式從檔案控制程式碼中讀取一個字元;
  • 使用 feof($handle) 判斷檔案控制程式碼是否到檔案的結尾處,否則繼續讀取檔案;
  • 當讀取完成後使用 fclose($handle) 關閉開啟的檔案控制程式碼,完成檔案讀取的所有操作。

總體來說,在讀取檔案時按照以上處理流程,基本上太容易出錯的。不過即便如此,還是有些重點需要我們小心處理:

  • 我們以什麼模式開啟一個檔案控制程式碼,示例中使用 $mode='rb' r(read) 只讀模式開個一個檔案控制程式碼(只讀模式下不能對檔案盡心寫入)。另外還有幾個常用模式可供使用:

    • r+ 讀寫模式
    • w(write) 覆蓋寫入
    • w+ 覆蓋讀寫
    • a(append) 追加寫入
    • a+ 追加讀寫
    • b 重點關注此模式,為增強專案可移植和健壯性,推薦所有模式新增「b」模式強制使用二進位制模式
    • 有關所有可用模式的說明可以從 模式 手冊中查詢。
  • 在執行檔案內容讀取時除了逐字元讀取(fgetc),要支援一下集中讀取形式:

  • 處理可以從控制程式碼中讀取檔案資料,PHP 還提供將整個檔案讀取的方法:

注意: 讀取檔案操作時我們推薦使用 file_get_contents

到這裡我們基本上就涵蓋了檔案讀取的所有知識點,相信大家對檔案讀取已經有了一個比較系統的認知。

下面我們進入到檔案寫入處理中,看看檔案寫入的正確姿勢。

2.2.2.5 讀取寫入

典型的檔案寫入流程基本上和檔案讀取流程一致:

    1. 開啟一個檔案控制程式碼;
    1. 使用檔案讀取函式向檔案中寫入內容;
    1. 寫入完成關閉控制程式碼。

依據慣例我們來看一個簡單的示例:

<?php
$filename = "F:\\php_workspace\\php-code-kata\\read.txt";

// 1. 開啟一個檔案控制程式碼;
$handle = fopen($filename, $mode = 'ab');

// 2. 使用檔案讀取函式向檔案中寫入內容
fwrite($handle, "hello filesystem to write!\n");

// 3. 寫入完成關閉控制程式碼;
fclose($handle);

注意:這裡我們以追加寫入的模式 $mode = 'ab' 寫入檔案內容。

檔案寫入就如同檔案讀取一樣的簡單,相信大家能夠輕鬆掌握這方面的知識。然而,我們顯示世界可能充滿了荊棘,稍不留神可能就會深陷泥沼。比如:

  • 我在寫入檔案時,同時其他人也在對同一個檔案進行寫入,怎麼辦?我們可以使用 flock($handle, LOCK_EX) 加鎖函式進行獨佔寫入。
  • 每次都需要 開啟檔案寫入再關閉 是在麻煩!有沒有更簡單的方式寫檔案呢?PHP 同樣為你考慮到了這點,所以提供了 file_put_contents($filename, $data [, LOCK_EX]) 將一個字串寫入檔案,同樣的它也支援獨佔寫入。

到這裡,我們基本上就學習完 PHP 檔案系統中大多數常用的函式了。然而就如我所說的那樣,現實世界總是殘酷的。尤其是在讀寫檔案時,經常會遇到各種各樣的錯誤,我們應該如何才能避免呢?

嗯,PHP 一樣為我們內建了檢測檔案有效性的函式,規避各種錯誤。

2.2.2.5 如何處理檔案許可權及檢測有效性
  • 檔案有效性檢測

檢測檔案的有效效能夠讓我們規避常見的開發錯誤,比如:

  1. 當相檔案中寫入資料時,是不是需要檢測它有可寫的許可權,並且它是不是一個檔案而非資料夾?
  2. 讀取檔案內容時,是不是需要檢視下我們能不能對其進行讀取?
  3. 在安裝專案時,我們是不是需要檢測已經依據例項配置檔案建立了實際的配置檔案呢?

這些內容都需要使用到檔案有效性檢測相關知識。

  1. 判斷檔案是否可寫我們有:is_writable($filename)SplFileInfo::isWritable()
  2. 路徑目錄判斷:is_dir(\$filename)SplFileInfo::isDir();檔案判斷:is_file($filename) 和 **SplFileInfo::isFile()
  3. 檢測檔案或目錄是否已經建立過,我們使用 file_exists($filename) 函式完成。
  • 如何修改檔案許可權

當我們能夠正確的檢測檔案是否存在時,我們還需要面對的問題時,如果我們的檔案當前使用者 不可寫入,我們應該如何修改許可權使其可寫呢?

這裡就涉及修改檔案許可權操作,之前我們在建立目錄是已經接觸過 umask 掩碼相關知識。這裡我們將講解那些已經建立的檔案許可權變更的方法。

通常,我們會使用 chmod($filename, $mode) 去修改一個檔案的許可權。

另外,還可以關注以下幾個許可權相關的處理函式:

以及,之前提到過的 umask 修改掩碼函式。

文章進行到這裡,其實基本上 PHP 檔案系統的所有知識都已經涉及到了。那麼,下回見吧?不不不...

為了應對實戰(面試需要),我們可能需要進一步對目錄遍歷做更進一步的研究。還記得我們之前使用過 scandir 來遍歷指定路徑中的檔案和目錄夾麼?

現在我們將使用物件導向的介面來重新實現一個許可權的目錄遍歷處理。

3 物件導向的目錄遍歷

使用物件導向的介面來遍歷目錄,是一個非常有意義的教程,這裡我們所涉及使用的介面包括:

話不多說,我們看下如何建立一個功能強大的支援遞迴迭代的目錄迭代程式:

/**
 * 目錄掃描
 * 
 * @method listContents($path, $recursive = false) 獲取目錄中所有檔案及資料夾
 */
class DirectoryScanner 
{

    /**
     * 獲取目錄中所有檔案及資料夾
     * 
     * @param $path      目錄
     * @param $recursive 遞迴獲取
     * 
     * @return array
     */
    public static function listContents($path, $recursive = false)
    {
        $iter = $recursive ? static::getRecursiveDirectoryIterator($path) : static::getDirectoryIterator($path);

        $result = [];
        foreach ($iter as $file) {
            if (in_array($file->getFilename(), ['.', '..'])) {
                continue;
            }

            $result[] = clone $file;
        }

        return $result;
    }

    /**
     * 獲取目錄迭代器
     * 
     * @param $path      目錄
     * 
     * @return DirectoryIterator::class
     */
    public static function getDirectoryIterator($path)
    {
        return new DirectoryIterator($path);
    }

    /**
     * 獲取遞迴目錄迭代器
     * 
     * @param $path      目錄
     * @param $mode      遍歷模式: RecursiveIteratorIterator::SELF_FIRST 從當前目錄開始遍歷;RecursiveIteratorIterator::CHILD_FIRST 從子目錄開始遍歷
     * 
     * @return RecursiveIteratorIterator::class
     */
    public static function getRecursiveDirectoryIterator($path, $mode = RecursiveIteratorIterator::SELF_FIRST )
    {
        return new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
            $mode
        );
    }
}

$path = 'F:\php_workspace\php-code-kata\direcotry-iterator\dir';

var_dump(DirectoryScanner::listContents($path));
var_dump(DirectoryScanner::listContents($path, true));

4 PHP 檔案系統思維導圖

檔案系統思維導圖

概覽

5 擴充套件閱讀

相關文章