Por que devo retroceder o iteratorIterator
Pergunta
$arrayIter = new ArrayIterator( array(1, 2) );
$iterIter = new IteratorIterator($arrayIter);
var_dump($iterIter->valid()); //false
var_dump($arrayIter->valid()); //true
Se eu ligar primeiro $ iteriter-> rewind (), então $ iteriter-> válido () é verdade. Estou curioso para saber por que exige que Rewind () seja chamado. Eu imagino que há boas razões para isso, mas eu esperaria que simplesmente iniciasse a iteração em qualquer estado que seja o iterador interno e deixasse como uma opção retroceder antes de iniciar a iteração.
Chamando a seguir () também parece colocá -lo em um estado "válido" (embora avise a próxima posição, sugerindo que estava anteriormente na primeira posição).
$arrayIter = new ArrayIterator(array(1,2));
$iterIter = new IteratorIterator($arrayIter);
$iterIter->next();
var_dump($iterIter->valid());
Novamente, estou curioso para saber por que preciso chamar Rewind (), apesar do iterador interno estar em um estado válido.
Solução
Com um novo iterador, a posição não é inicializada, simplesmente por motivo de desempenho, você pode empilhar iteradores em cima de outros iteradores, se todos eles rebobinarem durante a construção, haveria algum impacto no desempenho, além de alguns iteradores poderiam mudar seu primeiro valor após O construtor foi executado - o que é desconhecido para os iteradores mais distantes.
Os iteradores são geralmente executados por foreach () que faz um retrocesso () primeiro ...
Outras dicas
Enquanto estende o IteratorIterator
Classe de sobra de implementação de toda a interface do iterador e/ou para criar um decorador de um iterador que eu também estive com isso.
Esse decorador já é a solução para o problema, ele só precisa implementar a funcionalidade ausente para remover a inconsistência. Não há necessidade de um auto-rego:
class IteratorDecorator extends IteratorIterator
{
public function valid()
{
return $this->getInnerIterator()->valid();
}
}
Exemplo: se você tem um Iterator
objeto que é válido por padrão, por exemplo ArrayIterator
:
$it = new ArrayIterator(array(1));
var_dump($it->valid()); # bool(true)
$itit = new IteratorIterator($it);
var_dump($itit->valid()); # bool(false)
Isso mostra a inconsistência do IteratorIterator
implementação bem, o IteratorIterator
o objeto não reflete adequadamente o interior ArrayIterator
Estado. Usando o IteratorDecorator
pode curar isso:
$decor = new IteratorDecorator($it);
var_dump($decor->valid()); # bool(true)
E se você seguiu até aqui, aqui está outro caso especial que você pode querer considerar: se você não precisar rewind
Com o iterador interno, você pode apenas usar o NoRewindIterator
que retorna a validade correta também:
$noretit = new NoRewindIterator($it);
var_dump($noretit->valid()); # bool(true)
Levados em consideração a Johannes "nenhum argumento automático", isso faz sentido, pois o NoRewindIterator
espera que o iterador não seja rewimado e mostre a validade do iterador interno corretamente.
Mas como o IteratorDecorator
Mostra, eu também não faço nenhum tipo de auto-renda para remover a inconsistência.
Como @johannes disse, a posição não é inicializada no IteratorIterator
e, portanto, não é válido antes que nenhum outro de seus métodos seja executado nele ou seja usado com foreach ()
Tente fazer
var_dump( $iterIter->current() ); // NULL
var_dump( $iterIter->getInnerIterator()->current() ); // 1
E também
$iterIter->rewind();
var_dump( $iterIter->current() ); // 1
var_dump( $iterIter->getInnerIterator()->->current() ); // 1
E também observe que em um iteratorIterator unitário:
$iterIter->next(); // 2
var_dump( $iterIter->current()) ; // 2 (from NULL to 2)
var_dump( $iterIter->getInnerIterator()->current() ); // 2
Observe que $arrayIter
do seu snippet de código é idêntico a $iterIter->getInnerIterator()
.
Espero que esclareça um pouco de luz.