Lendo arquivos gigantes em PHP linha por linha sem matar o servidor

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: "Erro Fatal" e 2 gigabytes de logs



Imagine: você é um desenvolvedor, e um administrador de sistemas chega até você com um pendrive. Nele, há um arquivo de log do servidor web dos últimos seis meses. O tamanho? Modestos 2.5 gigabytes. "Preciso encontrar todos os endereços IP de onde houve tentativas de brute force", ele diz e vai tomar café.



Você abre a IDE e, mecanicamente, escreve:

$lines = file('huge_log.txt');foreach ($lines as $line) {    // processando...}

Aperta "Executar"... e vê uma tela branca no navegador, e nos logs de erro — Fatal Error: Allowed memory size exhausted. Familiar? Esta é a armadilha clássica em que quase todos os desenvolvedores PHP já caíram.



A pergunta "How to read a large file line by line?" no Stack Overflow acumulou centenas de milhares de visualizações por um bom motivo. Este é um daqueles problemas onde a solução "ingênua" mata o servidor, e a abordagem correta salva o projeto. Hoje, vamos analisar como ler arquivos enormes em PHP sem exceder o limite de memória e mantendo alto desempenho. Vamos lá!



Por que file() e file_get_contents() são o mal para grandes dados



Vamos logo desfazer o mito principal. As funções file() e file_get_contents() são funções gananciosas. Elas carregam o arquivo inteiro na memória RAM de uma só vez. Se o seu arquivo pesa 2 GB e o limite memory_limit no php.ini está definido para 128 MB (valor padrão para muitas hospedagens), você certamente receberá um erro.



Além disso, o PHP armazena strings em codificação, o que pode aumentar o consumo de memória em 2 a 4 vezes em relação ao tamanho do arquivo no disco. Ou seja, 2 GB de logs podem "consumir" de 6 a 8 GB de RAM!



Conclusão: se o tamanho do arquivo exceder 10-20% do seu memory_limit, esqueça o file(). Precisamos de uma abordagem de fluxo.



A maneira correta: fgets() e leitura por fluxo



O método mais confiável e eficiente é usar fopen() em conjunto com fgets(). Essas funções não leem o arquivo inteiro, mas uma linha de cada vez. Imagine que você está bebendo suco com um canudo — você não bebe o copo inteiro de um só gole, mas vai sugando aos poucos. A leitura por fluxo funciona da mesma forma.



Aqui está um exemplo básico, mas já funcional:



<?php$handle = fopen('huge_log.txt', 'r');if ($handle) {    while (($line = fgets($handle)) !== false) {        // Processa uma linha        echo $line;    }    fclose($handle);}?>


O que acontece aqui? fopen() abre o arquivo e retorna um ponteiro (resource). fgets() lê uma linha de cada vez até o caractere de nova linha (). A memória é alocada apenas para a linha atual, não para o arquivo inteiro. Após o processamento, a linha é destruída pelo coletor de lixo (ou sobrescrita).



Nuances importantes de desempenho

  • Tamanho do buffer: fgets() lê até 8192 bytes por padrão. Se as linhas forem mais longas, a função fará várias iterações. Para a grande maioria dos logs, isso é suficiente.
  • Verificação de false: Sempre use a comparação estrita !== false. Por quê? Porque uma linha pode ser vazia ("") ou conter "0", e while ($line = fgets($handle)) pode parar prematuramente.
  • Fechamento do arquivo: Sempre chame fclose(). Sim, o PHP fará isso sozinho ao final do script, mas se você estiver processando o arquivo em um loop ou em um script em segundo plano, descritores não fechados são um vazamento de recursos.


Super-carregamento: Geradores (yield) para reutilização



O gerenciamento manual de fopen/fclose é bom, mas se você precisar ler diferentes arquivos em diferentes partes do projeto, o código começará a se repetir. É aí que entram os geradores — um dos recursos mais elegantes do PHP.



Um gerador permite criar uma função que

Blogs

Book Recommendations