PHP incluye la clase nativa DirectoryIterator para recorrer el contenido de un directorio de forma eficiente. A diferencia de funciones como scandir() o glob(), esta clase es parte de la SPL (Standard PHP Library) y ofrece una interfaz orientada a objetos con métodos muy útiles para filtrar y obtener metadata de cada archivo.
¿Cuándo usar DirectoryIterator?
- Cuando necesitas recorrer un directorio y obtener propiedades de cada archivo (tamaño, fecha, tipo).
- Cuando quieres listar subdirectorios de forma recursiva.
- Cuando prefieres una API orientada a objetos en lugar de funciones procedurales.
Uso básico
Crear una instancia pasando la ruta del directorio y recorrerla con foreach:
$dir = new DirectoryIterator('/ruta/al/directorio');
foreach ($dir as $file) {
if ($file->isDot()) continue; // Omitir '.' y '..'
echo $file->getFilename() . PHP_EOL;
}
Nota: El método
isDot()es la forma idiomática de saltar las entradas.y.., en lugar de comparar el nombre manualmente.
Métodos más útiles
| Método | Descripción |
|---|---|
getFilename() | Nombre del archivo con extensión |
getBasename() | Igual que getFilename(), pero acepta sufijo |
getExtension() | Extensión del archivo (jpg, php, etc.) |
getSize() | Tamaño en bytes |
getMTime() | Timestamp de última modificación |
isFile() | true si es un archivo |
isDir() | true si es un directorio |
isDot() | true si es . o .. |
getPathname() | Ruta completa del archivo |
Ejemplo 1: Listar archivos con metadata
Un caso de uso común es obtener no solo los nombres, sino también el tamaño y la fecha de modificación de cada archivo:
function list_files_with_metadata(string $path): array
{
$data = [];
$dir = new DirectoryIterator(realpath($path));
foreach ($dir as $file) {
if ($file->isDot() || !$file->isFile()) continue;
$data[] = [
'name' => $file->getFilename(),
'size_kb' => round($file->getSize() / 1024, 2),
'modified' => date('Y-m-d H:i:s', $file->getMTime()),
'extension'=> $file->getExtension(),
];
}
return $data;
}
$files = list_files_with_metadata('./uploads');
// Salida ejemplo:
// [
// { "name": "foto.jpg", "size_kb": 142.5, "modified": "2024-03-10 14:22:00", "extension": "jpg" },
// { "name": "cv.pdf", "size_kb": 88.1, "modified": "2024-03-08 09:00:00", "extension": "pdf" }
// ]
header('Content-Type: application/json');
echo json_encode($files, JSON_PRETTY_PRINT);
Ejemplo 2: Recorrido recursivo de directorios
Para listar tanto archivos como subdirectorios de forma anidada, usamos recursividad. El resultado es un árbol que refleja la estructura real del sistema de archivos:
function list_directory_tree(string $path): array
{
$data = [];
$dir = new DirectoryIterator(realpath($path));
foreach ($dir as $file) {
if ($file->isDot()) continue;
if ($file->isDir()) {
// Subdirectorio: entramos recursivamente
$data[$file->getFilename()] = list_directory_tree($file->getPathname());
} else {
// Archivo: guardamos nombre y extensión
$data[] = [
'name' => $file->getFilename(),
'extension' => $file->getExtension(),
];
}
}
return $data;
}
$tree = list_directory_tree('./src');
// Salida ejemplo:
// {
// "Controllers": [
// { "name": "UserController.php", "extension": "php" }
// ],
// "Models": [
// { "name": "User.php", "extension": "php" }
// ]
// }
header('Content-Type: application/json');
echo json_encode($tree, JSON_PRETTY_PRINT);
Ejemplo 3: Filtrar archivos por extensión
Muchas veces solo te interesan ciertos tipos de archivo, por ejemplo, solo imágenes dentro de un directorio de uploads:
function list_files_by_extension(string $path, array $allowed_extensions): array
{
$data = [];
$dir = new DirectoryIterator(realpath($path));
foreach ($dir as $file) {
if ($file->isDot() || !$file->isFile()) continue;
if (in_array(strtolower($file->getExtension()), $allowed_extensions)) {
$data[] = [
'name' => $file->getFilename(),
'path' => $file->getPathname(),
'size_kb' => round($file->getSize() / 1024, 2),
];
}
}
return $data;
}
// Listar solo imágenes
$images = list_files_by_extension('./uploads', ['jpg', 'jpeg', 'png', 'webp', 'gif']);
// Listar solo documentos
$docs = list_files_by_extension('./uploads', ['pdf', 'docx', 'xlsx']);
header('Content-Type: application/json');
echo json_encode($images, JSON_PRETTY_PRINT);
Manejo de errores
DirectoryIterator lanza una UnexpectedValueException si el directorio no existe o no se tienen permisos de lectura. Es buena práctica envolverlo en un bloque try/catch:
function safe_list_files(string $path): array
{
try {
$dir = new DirectoryIterator(realpath($path));
} catch (UnexpectedValueException $e) {
// Directorio no encontrado o sin permisos
error_log("Error al abrir directorio: " . $e->getMessage());
return [];
}
$data = [];
foreach ($dir as $file) {
if ($file->isDot() || !$file->isFile()) continue;
$data[] = $file->getFilename();
}
return $data;
}
DirectoryIterator vs alternativas
| Herramienta | Recursividad nativa | Orientado a objetos | Filtros avanzados |
|---|---|---|---|
DirectoryIterator | No (manual) | ✅ | Básico |
RecursiveDirectoryIterator | ✅ | ✅ | Básico |
glob() | No | ❌ | Por patrón |
scandir() | No | ❌ | No |
Para proyectos donde necesitas recursividad sin escribirla tú mismo, considera RecursiveDirectoryIterator junto con RecursiveIteratorIterator:
$dir = new RecursiveDirectoryIterator('./src', FilesystemIterator::SKIP_DOTS);
$iter = new RecursiveIteratorIterator($dir);
foreach ($iter as $file) {
echo $file->getPathname() . PHP_EOL;
}