Что грустит в SPL (Standard PHP Library), так это какая-то смурная документация, не слишком актуальная к тому же. Поэтому работать с этой библиотекой приходится методом научного тыка. Т.е. не то что бы там пальцем в небо, но вот так, например можно узнать список методов класса, которые он умеет:
foreach( get_class_methods(DirectoryIterator) as $methodName)
{
echo $methodName.'<br />';
}
Вот и сегодня я наткнулся на странность которая бы не потребовала много времени, будь под рукой описание
Работаем с двумерным массивом и рекурсивным итератором. Пусть имеется такой массив:
$array = array(
array('name'=>'butch', 'sex'=>'male'),
array('name'=>'fido', 'sex'=>'male'),
array('name'=>'girly','sex'=>'female')
);
Конструкция foreach выдаст ожидаемый список всех значений массива:
$it=new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach($it as $key=>$value)
{
echo $key.' -- '.$value.'<br />';
}
name -- butch sex -- male name -- fido sex -- male name -- girly sex -- female
А вот while выдаст лишний элемент:
$it=new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
while($it->valid()){
echo $it->key().' -- '.$it->current().'<br/>';
$it->next();
}
0 -- Array name -- butch sex -- male name -- fido sex -- male name -- girly sex -- female
Наступает непонятко. Либо foreach в зависимости от ситуации делает next(), либо это rewind() ставит итератор в правильное начальное положение.
Перегрузив класс итератор получим ответ на наш вопрос:
class myrr extends RecursiveIteratorIterator
{
public function next()
{
echo 'next: ';
parent::next();
}
public function rewind()
{
parent::rewind();
echo 'rewind: ';
}
}
Конструкция foreach с этим итератором:
$it=new myrr(new RecursiveArrayIterator($array));
foreach($it as $key=>$value){
echo $key.' -- '.$value.'<br />';
}
выдаст:
rewind: name -- butch next: sex -- male next: name -- fido next: sex -- male next: name -- girly next: sex -- female next:
Ну и вызвав $it->rewind() явно перед while добъемся правильного результата и здесь.
Хотелось бы документированного поведения итераторов, особенно рекурсивного и кеширующего вроде того, что foreach вызывает то-то и то-то, наличие детей проверяется так-то, вызывается то-то, а то тычешься по api как мышонок
Вообще-то это скорее баг, чем фича -- новый итератор, должен быть в том же состоянии, что и после rewind()