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().
