Page 1 sur 1

L'interface RecursiveIterator

Posté : 24 avr. 2018, 14:49
par carte-sd
Bonjour à tous,
Comme il n'y a pas d'exemple sur le site officiel j'ai réalisé ce script un peu à l'aveugle. J'aimerais connaître l'avis d'experts car bien que ce soit fonctionnel je ne suis pas certain que ce soit la meilleure façon de faire (peut-être même que je n'ai rien compris, ce que je n'espère pas :oops:) :

Code : Tout sélectionner

<?php class monIterateur implements RecursiveIterator{ private $container=[], $key; /** * Iterator methods */ public function __construct(array $container){ $this->container = $container; $this->key = 0; } public function current(){ return $this->container[$this->key]; } public function key(){ return $this->key; } public function next(){ $this->key++; } public function rewind(){ $this->key = 0; } public function valid(){ return isset($this->container[$this->key]); } /** * RecursiveIterator methods */ public function getChildren(){ return new self($this->container[$this->key]); } public function hasChildren(){ return is_array($this->container[$this->key]); } } function iterate(RecursiveIterator $iterator):Generator{ while($iterator->valid()): if($iterator->hasChildren()): yield from iterate($iterator->getChildren()); else: $tabs = str_repeat("\t", (int) substr($iterator->current(), 1, 1)); yield $tabs.$iterator->current().'<br />'; endif; $iterator->next(); endwhile; } $mi = new monIterateur([ '[0]b0nj0ur', [ '[1]hell0', '[1]PHP', [ '[2]tr0isième', '[2]niv0', '[2]tr0 dark' ], '[1]suite' ], '[0]...et fin' ]); print '<pre>'; foreach(iterate($mi) as $value): print $value; endforeach; print '</pre>'; /* Résultat obtenu : [0]b0nj0ur [1]hell0 [1]PHP [2]tr0isième [2]niv0 [2]tr0 dark [1]suite [0]...et fin */

Re: L'interface RecursiveIterator

Posté : 24 avr. 2018, 22:41
par tesmet
Hello. C'est peut-être suffisant si le code est fonctionnel et que tu le maitrises ?

Pour moi yield est une classe pseudo-Iterator (Generator) qui n'a probablement pas été pensée pour être mixée avec de véritables *Iterator* mais plutôt pour faire simple et rapide et depuis PHP7 on a en effet yield from qui permet de faire du récursif en pseudo-Iterator.
function iterate_recursive($array) { 
  foreach($array as $value) {
    if(is_array($value)) yield from iterate_recursive($value); // PHP7
    else yield $value;
  }
}
$array = ['[0]b0nj0ur',['[1]hell0','[1]PHP',['[2]tr0isième','[2]niv0','[2]tr0 dark'],'[1]suite'],'[0]...et fin0'];
echo '<pre>';
foreach(iterate_recursive($array) as $value) {
  echo str_repeat("\t", $value{1}), "$value\n";
}
echo '</pre>';

Il y a aussi des classes PHP qui implémentent les interfaces *Iterator* et la classe RecursiveArrayIterator est à peu près identique à ta classe monIterateur. Si le but est d'émuler la fonction array_walk_recursive() en OO pour foreach() avec RecursiveArrayIterator, alors il faut lui adjoindre la classe RecursiveIteratorIterator.
$array = ['[0]b0nj0ur',['[1]hell0','[1]PHP',['[2]tr0isième','[2]niv0','[2]tr0 dark'],'[1]suite'],'[0]...et fin0'];
echo '<pre>';
foreach(new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $value) {
  echo str_repeat("\t", $value{1}), "$value\n";
}
echo '</pre>';

J'espère ne pas être trop à coté du sujet.
// identation automatique selon un pseudo-Iterator récursif
function tableau_recursif($tableau, $marge = 0) { 
  foreach($tableau as $valeur) {
    if(is_array($valeur)) {
      $marge++;
      yield from tableau_recursif($valeur, $marge); // PHP7
      $marge--;
    }
    else (yield $marge => $valeur);
  }
}
$tableau = ['b0nj0ur',['hell0','PHP',['tr0isième','niv0','tr0 dark'],'suite'],'...et fin0'];
echo '<pre>';
foreach(tableau_recursif($tableau) as $marge => $valeur) {
  echo str_repeat("\t", $marge), "[$marge]$valeur\n";
}

echo '<hr>';

// extraire en OO via une orgie de *Iterator* les fichiers PHP 
// du répertoire parent ainsi que tous les sous-répertoires
class RecursiveFilterFilesPHP extends RecursiveFilterIterator {
  public function accept() {
    return
      $this->hasChildren() ||
      strtolower(pathinfo($this->current(), PATHINFO_EXTENSION)) == 'php';
  }
}
$my_PHP_files = new RecursiveFilterFilesPHP(new RecursiveDirectoryIterator('../'));
foreach(new RecursiveIteratorIterator($my_PHP_files) as $value) {
  echo "$value\n";
}
echo '</pre>';

Re: L'interface RecursiveIterator

Posté : 29 avr. 2018, 11:54
par carte-sd
Bonjour @tesmet,
Je suis conscient que ma question peut surprendre, je passe mon temps à réinventer la roue, je teste absolument tout ce que je lis.
Non, ta réponse n'est pas à côté, elle m'a permis de découvrir un point de vue différent et de réaliser que j'ai bien compris le principe. Il y a cependant un petit problème avec ton dernier itérateur, il n'est pas récursif. Il faut réécrire les méthodes hasChildren() et getChildren() afin qu'il le devienne :

Code : Tout sélectionner

<?php class recursiveFilterFilesPHP extends RecursiveFilterIterator{ public function hasChildren(){ return is_dir($this->getInnerIterator()->current()->getPathname()); } public function getChildren(){ return new self(new RecursiveDirectoryIterator($this->getInnerIterator()->current()->getPathname(), FilesystemIterator::SKIP_DOTS)); } public function accept(){ return $this->hasChildren() || strtolower(pathinfo($this->getInnerIterator()->current(), PATHINFO_EXTENSION) === 'php'); } } function iterate($rffp){ $rffp->rewind(); while(true === $rffp->valid()): if(true === $rffp->hasChildren()): iterate($rffp->getChildren()); else: print $rffp->current().'<br />'; endif; $rffp->next(); endwhile; } $rdi = new RecursiveDirectoryIterator(__DIR__, FilesystemIterator::SKIP_DOTS); // Ne pas oublier le flag sinon boucle infinie $rffp = new recursiveFilterFilesPHP($rdi); iterate($rffp);
Merci