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étodoDescripció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

HerramientaRecursividad nativaOrientado a objetosFiltros avanzados
DirectoryIteratorNo (manual)Básico
RecursiveDirectoryIteratorBásico
glob()NoPor patrón
scandir()NoNo

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;
}