Pergunta

Por que meu script PHP está travado?

$path = tempnam(sys_get_temp_dir(), '').'.txt';
$fileInfo = new \SplFileInfo($path);
$fileObject = $fileInfo->openFile('a');
$fileObject->fwrite("test line\n");
$fileObject2 = $fileInfo->openFile('r');
var_dump(file_exists($path));          // bool(true)
var_dump(file_get_contents($path));    // string(10) "test line
                                       // "
var_dump(iterator_count($fileObject2)); // Hangs on this

Se eu excluir a última linha (iterator_count(...) e substitua-o por isto:

$i = 0;
$fileObject2->rewind();
while (!$fileObject2->eof()) {
    var_dump($fileObject2->eof());
    var_dump($i++);
    $fileObject2->next();
}

// Output:
// bool(false)
// int(0)
// bool(false)
// int(1)
// bool(false)
// int(2)
// bool(false)
// int(3)
// bool(false)
// int(4)
// ...

O $fileObject->eof() sempre retorna falso, então recebo um loop infinito.

Por que essas coisas estão acontecendo?Preciso obter uma contagem de linhas.

Foi útil?

Solução

Por que essas coisas estão acontecendo?

Você está experimentando uma peculiaridade na maneira como o SplFileObject a aula está escrita.Sem ligar next() e current() métodos - usando o padrão (0) sinalizadores — o iterador nunca avança.

O iterator_count() função nunca chama current();verifica valid() e chamadas next() apenas.Seus loops personalizados chamam apenas um ou outro current() e next().

Isso deve ser considerado um bug (seja no próprio PHP, ou uma falha na documentação) e o código a seguir deve (e não) funciona como esperado.Eu convido você para denunciar este mau comportamento.

// NOTE: This currently runs in an infinite loop!
$file = new SplFileObject(__FILE__);
var_dump(iterator_count($file));

Soluções alternativas

Um passo rápido para fazer as coisas andarem é definir o READ_AHEAD bandeira no objeto.Isto fará com que next() método para ler a próxima linha disponível.

$file->setFlags(SplFileObject::READ_AHEAD);

Se, por qualquer motivo, você não quiser que o Leia adiante comportamento, então você deve ligar para ambos next() e current() você mesmo.

De volta ao problema original de dois SplFileObjects

O seguinte agora deve funcionar conforme o esperado, permitindo anexar a um arquivo e ler sua contagem de linhas.

<?php
$info = new SplFileInfo(__FILE__);
$write = $info->openFile('a');
$write->fwrite("// new line\n");
$read = $info->openFile('r');
$read->setFlags(SplFileObject::READ_AHEAD);
var_dump(iterator_count($read));

Outras dicas

EDITADO 01

Se o que você precisa é do número de linhas dentro do arquivo:

<?php

$path = tempnam(sys_get_temp_dir(), '').'.txt';

$fileInfo = new SplFileInfo($path);
$fileObject = $fileInfo->openFile('a+');

$fileObject->fwrite("Foo".PHP_EOL);
$fileObject->fwrite("Bar".PHP_EOL);

echo count(file($path));  // outputs 2

?>


EDITADO 02

Este é o seu código acima, mas sem entrar em loops infinitos devido ao ponteiro do arquivo:

<?php

$path = tempnam(sys_get_temp_dir(), '').'.txt';

$fileInfo = new SplFileInfo($path);
$fileObject = $fileInfo->openFile('a+');

$fileObject->fwrite("Foo".PHP_EOL);
$fileObject->fwrite("Bar");

foreach($fileObject as $line_num => $line) {
    echo 'Line: '.$line_num.' "'.$line.'"'."<br/>";
}
echo 'Total Lines:' . $fileObject->key();

?>

Resultados

Linha:0 "Foo"

Linha:1 "Barra"

Total de linhas: 2



RESPOSTA ORIGINAL

A lógica aplicada estava um pouco errada.Simplifiquei o código:

<?php

// set path to tmp with random file name
echo $path = tempnam(sys_get_temp_dir(), '').'.txt';
echo "<br/>";

// new object
$fileInfo = new \SplFileInfo($path);

// open to write
$fileObject = $fileInfo->openFile('a');

// write two lines
$fileObject->fwrite("Foo".PHP_EOL);
$fileObject->fwrite("Bar".PHP_EOL);

// open to read
$fileObject2 = $fileInfo->openFile('r');

// output contents
echo "File Exists: " .file_exists($path);
echo "<br/>";
echo "File Contents: " . file_get_contents($path);
echo "<br/>";

// foreach line get line number and line contents 
foreach($fileObject2 as $line_num => $line) {
  echo 'Line: '.$line_num;
  echo ' With: "'.$line.'" is the end? '.($fileObject2->eof()?'yes':'no')."<br>";
}

?>

Saídas:

/tmp/EAdklY.txt

O arquivo existe:1

Conteúdo do arquivo:Foo Bar

Linha:0 Com:"Foo" é o fim?não

Linha:1 Com:"Bar" é o fim?não

Linha:2 Com:"" é o fim?sim

Embora possa parecer contra-intuitivo, com o PHP 5.3.9, isto:

<?php
$f = new SplFileObject('test.txt', 'r');
while (!$f->eof()) {
    $f->next();
}

while seja um loop infinito e nunca saia.

O seguinte será encerrado quando o final do arquivo for atingido:

<?php
$f = new SplFileObject('test.txt', 'r');
while (!$f->eof()) {
    $f->current();
}

Então:

$i = 0;
$fileObject2->rewind();
while (!$fileObject2->eof()) {
    var_dump($fileObject2->eof());
    var_dump($i++);
    $fileObject2->next();
}

deve ser reescrito como:

$fileObject2->rewind();
while (!$fileObject2->eof()) {
    $fileObject2->current();
}

$i = $fileObject2->key();
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top