Registo não é sinónimo de linha física
Um ficheiro de operações fictício tem as colunas id,status,amount. Uma descrição entre aspas pode conter vírgulas e, noutros contratos CSV, até uma mudança de linha. Por isso, explode sobre uma linha não substitui um parser CSV. Declara separador, enclosure, escape e codificação como parte do contrato entre produtor e consumidor. No dialecto desta aula, o escape é explicitamente vazio e as aspas internas são duplicadas. A omissão desse argumento está obsoleta desde PHP 8.4, segundo a documentação; os testes locais desta expansão usam PHP 8.3.17. Distingue o resultado de parsing da validação: obter três campos não prova tipos, intervalos, identidade ou autorização para os importar.
Validar antes de atribuir colunas
Antes de ler dados com posições fixas, compara o cabeçalho com a sequência contratada. Se o fornecedor enviar amount,id,status, a quantidade de colunas é correta mas a atribuição ficaria errada. Há duas políticas possíveis: rejeitar a ordem divergente ou criar um mapeamento por nomes depois de validar ausências e duplicados. Escolhe uma e testa-a. Trata uma linha vazia [null] segundo uma regra explícita; não a confundas com false. Para cada registo, verifica a quantidade de campos antes de os combinar com os nomes. Guarda um número de registo e uma categoria de rejeição, sem copiar todos os valores para os logs. Um relatório de rejeições deve permitir corrigir o lote sem expor dados desnecessários.
Memória depende também do consumidor
Um gerador produz valores quando o consumidor avança. Isso permite ler e validar um registo de cada vez, mas não garante memória constante em todos os desenhos. Se o consumidor chamar iterator_to_array, volta a acumular a coleção inteira. Se um campo puder ocupar centenas de megabytes, o registo atual também continua a ser um problema. Define limites de ficheiro e registo numa camada de ingestão adequada ao dialecto; partir cegamente CSV por newline pode cortar um campo válido. Para compreender o comportamento, começa com três registos pequenos, observa quando cada um é produzido e só depois usa um ficheiro sintético maior. Mede a cadeia completa, incluindo resultados, rejeições e logs retidos em memória.
Propriedade de recursos e falhas diferidas
Criar um Generator não executa antecipadamente toda a importação. Uma exceção pode surgir no segundo avanço, pelo que um try em torno apenas da criação não cobre o consumo posterior. Coloca o tratamento na fronteira que percorre o leitor e consegue decidir se o lote deve parar. O exemplo atribui o stream ao chamador: ele abre, percorre e fecha num finally. O gerador limita-se a usar o recurso emprestado. Assim, um break ou uma exceção no consumidor não deixa a responsabilidade de fecho ambígua. Não retomes um gerador depois de o seu stream ser fechado. Num gerador que seja dono do recurso, a retenção de uma referência suspensa exige cuidado adicional com o momento da limpeza.
Escrita, substituição e resultado do lote
Um relatório antigo pode desaparecer antes de a primeira linha nova ser escrita: abrir com modo w trunca um destino existente. Valida primeiro e prepara a saída num ficheiro temporário do diretório controlado. O desenho de publicação deve depois definir concorrência, permissões, substituição e durabilidade para o sistema de ficheiros real; não assumes que qualquer rename entre destinos é atómico. Em streams com escrita parcial, acompanha o número de bytes efetivamente escrito e continua apenas com o sufixo pendente. Trata false e zero sem progresso de forma explícita, com espera limitada se o stream a justificar. Fechar um ficheiro não confirma uma transação de negócio nem torna reversíveis efeitos externos já enviados durante o lote.
Laboratório e critérios de aceitação
Executa o exemplo com dois registos válidos e prevê a soma: 120 mais 30 resulta em 150 unidades inteiras. Depois altera a ordem do cabeçalho, remove uma coluna e introduz uma linha vazia. A primeira mudança e a segunda devem falhar; a linha vazia é ignorada por política explícita. O leitor ensina estrutura, não um importador financeiro completo: antes de persistir, seriam necessários validação de valores, controlo de duplicados, autorização e uma política de lote parcial. Compara também o consumo direto com a conversão para array. O resumo operacional deve dizer quantos registos foram aceites, rejeitados e confirmados; não confundas registos lidos com operações efetivamente concluídas. Liga este raciocínio à aula de transações e outbox.
<?php
declare(strict_types=1);
function records($stream): Generator {
$header = fgetcsv($stream, null, ',', '"', '');
if ($header !== ['id', 'status', 'amount']) {
throw new UnexpectedValueException('Unexpected header');
}
while (($row = fgetcsv($stream, null, ',', '"', '')) !== false) {
if ($row === [null]) { continue; }
if (count($row) !== 3) {
throw new UnexpectedValueException('Expected three columns');
}
yield array_combine($header, $row);
}
if (!feof($stream)) { throw new RuntimeException('Read failed'); }
}
$stream = fopen('php://memory', 'w+');
if ($stream === false) { throw new RuntimeException('Open failed'); }
try {
$input = "id,status,amount\nA1,ready,120\nA2,ready,30\n";
if (fwrite($stream, $input) !== strlen($input)) {
throw new RuntimeException('Fixture write incomplete');
}
rewind($stream);
$total = 0;
foreach (records($stream) as $row) {
if (!preg_match('/^[0-9]{1,6}$/D', $row['amount'])) {
throw new UnexpectedValueException('Expected bounded integer units');
}
$total += (int) $row['amount'];
}
echo $total;
} finally {
fclose($stream);
}
Um batch lê o número esperado de linhas mas publica montantes trocados com IDs. Validar o cabeçalho teria impedido a atribuição errada antes da primeira operação.
Armadilhas comuns
Acumular o gerador num array, tratar zero como fim, ignorar cabeçalhos, fechar recursos de outro componente ou presumir que w conserva a versão anterior.
Tópicos relacionados: Tipos e comparações explícitas · Arrays e funções com intenção · Transações e falhas parciais em PDO
Processamento incremental exige um contrato de registo, limites de entrada, consumo disciplinado e responsabilidade explícita pelos recursos.
Referência: PHP manual: fgetcsv · PHP 8.5 reference; DR PHP 2026.3; new fixtures executed on PHP 8.4.4 / SQLite 3.51.2