?

Log in

No account? Create an account
Craftman

PHP-SPL tricks

Что грустит в 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()

Comments