opendir() 是 PHP 目錄操作的**入口函數**,回傳**目錄控制代碼 (resource)** 供 readdir()、rewinddir()、closedir() 使用。本文詳細說明語法、參數、回傳值、錯誤處理、FTP/HTTP 封裝協定支援、資源釋放最佳實務,並提供遞迴掃描基礎範例。
opendir(string $path, ?resource $context = null): resource|false
$path:目錄路徑,支援本地路徑、ftp://、https://、ssh2.sftp:// 等封裝協定 (需對應擴充套件)。$context:串流上下文 (如 FTP 認證、HTTP Header、SSL 驗證)。resource (目錄控制代碼),失敗回傳 false 並觸發 E_WARNING。<?php
$dir = './';
$handle = @opendir($dir);
if ($handle === false) {
die("無法開啟目錄: $dir (權限不足或不存在)");
}
echo "<ul>";
while (($entry = readdir($handle)) !== false) {
// 過濾 . 和 ..
if ($entry === '.' || $entry === '..') continue;
echo "<li>$entry</li>";
}
echo "</ul>";
closedir($handle); // 必須釋放資源
$ftpPath = 'ftp://user:pass@ftp.example.com/public_html/';
$ctx = stream_context_create([
'ftp' => ['overwrite' => true, 'resume_pos' => 0]
]);
$handle = @opendir($ftpPath, $ctx);
if ($handle) {
while (($file = readdir($handle)) !== false) {
if ($file !== '.' && $file !== '..') echo "$file<br>";
}
closedir($handle);
} else {
echo 'FTP 連線失敗';
}
$handle = @opendir('https://example.com/assets/');
if ($handle) {
while (($file = readdir($handle)) !== false) echo "$file<br>";
closedir($handle);
}
function scanDirRecursive(string $dir, int $depth = 0): array {
$handle = @opendir($dir);
if (!$handle) return [];
$result = [];
while (($entry = readdir($handle)) !== false) {
if ($entry === '.' || $entry === '..') continue;
$full = $dir . '/' . $entry;
$item = ['name' => $entry, 'path' => $full, 'depth' => $depth];
if (is_dir($full)) {
$item['type'] = 'dir';
$item['children'] = scanDirRecursive($full, $depth + 1);
} else {
$item['type'] = 'file';
$item['size'] = filesize($full);
}
$result[] = $item;
}
closedir($handle);
return $result;
}
print_r(scanDirRecursive('src'));
| 原則 | 做法 | |
|---|---|---|
| 必須關閉 | 所有路徑 (正常/break/continue/exception) 皆呼叫 closedir($handle) | |
| try/finally | PHP 5.5+ 建議包裹確保釋放 | |
| 避免洩漏 | 長期運行腳本 (daemon) 定期檢查 get_resources() 未釋放 handle | |
| 錯誤抑制 | 用 @opendir() 避免 E_WARNING 干擾輸出,自行檢查回傳值 |
| 警告/錯誤 | 原因 | 排查 |
|---|---|---|
| failed to open dir: Permission denied | 目錄權限 700/500、SELinux/AppArmor 限制 | is_readable()、檢查 ACL、調整 context |
| failed to open dir: No such file or directory | 路徑錯誤、相對路徑基準不對 | 用 realpath() 正規化 |
| ftp:// wrapper disabled | php.ini allow_url_fopen=Off 或未編譯 ftp 擴充 | 啟用 allow_url_fopen、安裝 php-ftp |
opendir() 以**極簡 API、通用封裝協定支援、手動資源控制**成為底層目錄操作基石;現代專案優先使用 DirectoryIterator 享受物件導向優勢,僅在需**自定遞迴邏輯、串流上下文控制、舊版本相容**時直接呼叫。