SplFileObject y file() en PHP: leer ficheros línea a línea con elegancia

PHP ofrece dos formas orientadas a objetos para trabajar con ficheros en modo lectura línea a línea: la función file(), que devuelve un array con todas las líneas, y SplFileObject, que es un iterador y permite procesar el fichero sin cargarlo todo en memoria. Cada una tiene su momento ideal.

file(): leer todas las líneas en un array

<?php
// Carga todas las líneas del fichero en un array
$lineas = file('/var/datos/ciudades.txt');

// Por defecto incluye el salto de línea al final de cada elemento
// FILE_IGNORE_NEW_LINES lo elimina; FILE_SKIP_EMPTY_LINES ignora líneas vacías
$lineas = file('/var/datos/ciudades.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

foreach ($lineas as $i => $ciudad) {
    echo ($i + 1) . '. ' . trim($ciudad) . "n";
}
?>

Es cómoda para ficheros pequeños donde el procesamiento completo en memoria es aceptable. Para ficheros de varios MB o más, SplFileObject es preferible.

SplFileObject: iterador de ficheros

<?php
$fichero = new SplFileObject('/var/datos/ciudades.txt');
$fichero->setFlags(SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

foreach ($fichero as $linea) {
    echo trim($linea) . "n";
}
// Al terminar el foreach, el iterador cierra el fichero automáticamente
?>

Procesar ficheros grandes sin agotar memoria

La ventaja real de SplFileObject frente a file() es que solo mantiene una línea en memoria en cada iteración:

<?php
$logFile = new SplFileObject('/var/log/nginx/access.log');
$logFile->setFlags(SplFileObject::DROP_NEW_LINE | SplFileObject::SKIP_EMPTY);

$errores500 = 0;
foreach ($logFile as $linea) {
    if (str_contains((string) $linea, ' 500 ')) {
        $errores500++;
    }
}
echo "Errores 500: $errores500n";
// Funciona con logs de varios GB sin agotar la memoria del proceso
?>

Parsear CSV con READ_CSV

SplFileObject puede parsear CSV directamente al activar el flag READ_CSV:

<?php
$csv = new SplFileObject('/var/datos/productos.csv');
$csv->setFlags(
    SplFileObject::READ_CSV   |  // parsear como CSV
    SplFileObject::SKIP_EMPTY |  // ignorar líneas vacías
    SplFileObject::DROP_NEW_LINE // sin salto de línea al final
);
$csv->setCsvControl(',', '"', '\'); // separador, enclosure, escape

// Saltar la cabecera
$csv->current(); // ['id','nombre','precio','stock']
$csv->next();

foreach ($csv as $fila) {
    if (!is_array($fila) || count($fila) < 4) continue;
    [$id, $nombre, $precio, $stock] = $fila;
    echo "$nombre: $precio € ($stock uds.)n";
}
?>

SplFileInfo: metadatos de un fichero

SplFileInfo encapsula los metadatos de un fichero o directorio sin abrirlo:

<?php
$info = new SplFileInfo('/var/www/app/public/logo.png');

echo $info->getFilename();        // logo.png
echo $info->getExtension();       // png
echo $info->getBasename('.png');  // logo  (sin extensión)
echo $info->getPath();            // /var/www/app/public
echo $info->getRealPath();        // ruta absoluta resuelta
echo $info->getSize();            // tamaño en bytes
echo $info->getMTime();           // timestamp de última modificación
echo $info->isFile();             // true
echo $info->isDir();              // false
echo $info->isReadable();         // true/false
echo $info->isWritable();         // true/false

// Obtener un SplFileObject desde SplFileInfo
$fo = $info->openFile('r');
?>

Error habitual con SplFileObject en ficheros grandes

<?php
// MAL: guardar SplFileObject en variable sin usarlo como iterador
// puede dejar el fichero abierto si no se destruye el objeto
function contarLineas(string $ruta): int
{
    $f = new SplFileObject($ruta);
    $f->seek(PHP_INT_MAX);          // salta al final
    return $f->key();               // número de la última línea = total - 1
    // el fichero se cierra al salir de la función (destructor de $f)
}

// BIEN: usar en su contexto inmediato
$fichero = new SplFileObject('/ruta/grande.log');
$fichero->setFlags(SplFileObject::DROP_NEW_LINE | SplFileObject::SKIP_EMPTY);
foreach ($fichero as $linea) {
    // procesar...
}
unset($fichero); // cierre explícito si se necesita liberar antes
?>

La documentación de SplFileObject y la de SplFileInfo muestran todos los flags disponibles y los métodos para acceder a posiciones arbitrarias del fichero con seek().

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP