# PHP目录遍历:scandir() 和 DirectoryIterator 详解
在PHP开发中,经常需要处理文件和目录操作。今天我们就来深入探讨PHP中两种常用的目录遍历方法:`scandir()`函数和`DirectoryIterator`类。
## 一、scandir()函数基础用法
`scandir()`是PHP内置的一个简单直接的目录遍历函数,它会返回指定路径下的所有文件和目录的数组。
```php
$dir = '/path/to/directory';
$files = scandir($dir);
print_r($files);
```
### 1. 过滤特殊目录项
默认情况下,`scandir()`会返回`.`(当前目录)和`..`(上级目录)这两个特殊项,我们可以过滤掉它们:
```php
$files = array_diff(scandir($dir), ['.', '..']);
```
### 2. 排序选项
`scandir()`第二个参数可以指定排序方式:
```php
// 按字母降序排列
$files = scandir($dir, SCANDIR_SORT_DESCENDING);
// 按字母升序排列(默认)
$files = scandir($dir, SCANDIR_SORT_ASCENDING);
// 不排序
$files = scandir($dir, SCANDIR_SORT_NONE);
```
### 3. 实用示例:获取目录下所有图片
```php
$images = array_filter(scandir($dir), function($file) {
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
return in_array($ext, ['jpg', 'png', 'gif']);
});
```
## 二、DirectoryIterator类更强大的功能
`DirectoryIterator`是PHP SPL(标准PHP库)提供的面向对象方式遍历目录的类,它提供了更丰富的功能。
### 1. 基本用法
```php
$dir = new DirectoryIterator('/path/to/directory');
foreach ($dir as $fileinfo) {
if (!$fileinfo->isDot()) {
echo $fileinfo->getFilename() . "\n";
}
}
```
### 2. 常用方法
`DirectoryIterator`提供了许多有用的方法:
- `isDot()` - 是否是`.`或`..`
- `isFile()` - 是否是文件
- `isDir()` - 是否是目录
- `isLink()` - 是否是符号链接
- `getSize()` - 获取文件大小
- `getMTime()` - 获取修改时间
- `getType()` - 获取类型(file, dir, link等)
- `getExtension()` - 获取扩展名
### 3. 递归遍历示例
结合`RecursiveDirectoryIterator`可以实现递归遍历:
```php
$dir = new RecursiveDirectoryIterator('/path/to/directory');
$iterator = new RecursiveIteratorIterator($dir);
foreach ($iterator as $file) {
if ($file->isFile()) {
echo $file->getPathname() . " (" . $file->getSize() . " bytes)\n";
}
}
```
## 三、性能与适用场景对比
1. **scandir()优点**:
- 简单易用
- 一次性获取所有文件和目录
- 适合处理小规模目录
2. **DirectoryIterator优点**:
- 更丰富的文件信息
- 更好的内存效率(特别是大目录)
- 面向对象接口
- 支持递归遍历
3. **性能考虑**:
对于非常大的目录,`DirectoryIterator`通常更高效,因为它不是一次性加载所有条目到内存。
## 四、实战应用案例
### 1. 网站文件浏览器
```php
function listFiles($path) {
$result = [];
$iterator = new DirectoryIterator($path);
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDot()) continue;
$result[] = [
'name' => $fileinfo->getFilename(),
'type' => $fileinfo->getType(),
'size' => $fileinfo->isFile() ? formatSize($fileinfo->getSize()) : '',
'modified' => date('Y-m-d H:i:s', $fileinfo->getMTime())
];
}
return $result;
}
function formatSize($bytes) {
// 格式化文件大小显示
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 2) . ' KB';
} else {
return $bytes . ' bytes';
}
}
```
### 2. 清理日志文件
```php
function cleanupOldLogs($dir, $days = 30) {
$cutoff = time() - ($days * 24 * 60 * 60);
$iterator = new DirectoryIterator($dir);
foreach ($iterator as $file) {
if ($file->isFile() &&
$file->getExtension() === 'log' &&
$file->getMTime() < $cutoff) {
unlink($file->getPathname());
}
}
}
```
## 五、安全注意事项
1. 始终验证用户提供的路径
2. 使用`realpath()`解析路径
3. 检查目录是否可读
4. 处理异常情况
```php
function safeScanDir($path) {
$realPath = realpath($path);
if ($realPath === false || !is_dir($realPath) || !is_readable($realPath)) {
throw new Exception("Invalid or inaccessible directory: " . $path);
}
return scandir($realPath);
}
```
## 结语
无论是简单的`scandir()`还是功能更强大的`DirectoryIterator`,PHP都提供了灵活的方式来处理目录遍历任务。根据你的具体需求选择合适的工具,可以提高代码效率和可维护性。
**小技巧**:在PHP 7.0+中,还可以考虑使用`Generator`结合这些方法,以进一步优化内存使用。
希望这篇文章能帮助你在PHP项目中更高效地处理文件和目录操作!