Général

Les PCRE

Les POSIX

Pratique

Linux

Spécial php

Les billets de fred

Créer un itérateur avec PHP5

Quand vous parcourez un tableau en PHP, vous employez la structure de langage "foreach" (lire : Franchement, t'es trop for !). Il serait intéressant de pouvoir en faire de même avec n'importe quelle donnée comme le résultat d'une requête SQL par exemple.

La solution la plus simple serait de mettre le résultat dans un tableau, puis de le parcourir avec "foreach", mais c'est un peu idiot car on doit parcourir deux fois les données !

Pour résoudre ce problème, nous allons exploiter une nouveauté de "foreach" introduit dans PHP5 : les itérateurs. En effet, depuis la sortie de la version 5 de PHP, "foreach" n'attend plus obligatoirement un tableau, mais il peut prendre un objet en paramètre. Si cet objet n'est pas un itérateur (je vais vous expliquer après ce qu'est un itérateur), "foreach" va retourner l'ensemble des propriétés publiques de l'objet.

Exemple :
<?php
class Point {
  public $x = null;
  public $y = null;
  function set( $x, $y ) {
    $this->x = $x;
    $this->y = $y;
  }
}

$point = new Point();
$point->set( 3, 7 );
foreach( $point as $clef => $valeur ) {
  echo "$clef => $valeur<br />";
}
?>

Ce qui donne :
x => 3
y => 7

Maintenant, si la classe de mon objet implémente l'interface "Iterator", "foreach" ne va plus parcourir les propriétés de mon objet, mais il va appeler différentes méthodes permettant d'adapter son comportement.

Les méthodes introduites par Iterator sont :

    * current : retourne l'élément courant
    * key : retourne la valeur de la clef courante
    * next : positionne sur l'élément suivant (ne retourne rien)
    * rewind : positionne sur le premier élément (ne retourne rien)
    * valid : indique si on a atteint la fin de la liste d'éléments.

Observez cet exemple :
<?php

class MonIterateur implements Iterator {
    private $compteur = 0;
    function current(){
        echo 'current()<br />';
        return 'element de ' . $this->compteur;
    }

    function key() {
        echo 'key()<br />';
        return 'clef ' . $this->compteur;
    }

    function next(){
        echo 'next()<br />';
        $this->compteur++;
    }

    function rewind(){
        echo 'rewind()<br />';
    }

    function valid() {
        echo 'valid()<br />';
        return $this->compteur < 2;
    }
}

$iterateur = new MonIterateur();

foreach( $iterateur as $clef => $valeur ) {
  echo "*** $clef => $valeur ***<br />";
}

?>

Nous obtenons le résultat suivant :
rewind()
valid()
current()
key()
*** clef 0 => element de 0 ***
next()
valid()
current()
key()
*** clef 1 => element de 1 ***
next()
valid()

Cela nous permet de déduire cet enchaînement :

Iterateur

Remarque : Contrairement à ce que l'on pourrait penser, si vous n'utilisez par la clef de votre itérateur, la méthode "key()" est malgré tout appelée.

Sur cette base, voici le code que l'on pourrait implémenter pour parcourir le résultat d'une requête SQL "SELECT" (utilisation de l'API mysqli) :
<?php

class Select implements Iterator {
    private $result = null;
    private $current = null;
    private $key = 0;
    
    protected $sql = null;
    
    // Constructeur de la classe prennant la requête SQL
    // en paramètre.
    function __construct( $sql ) {
        // Permet de ne pas refaire de connexion
        // à la base plusieurs fois
        static $mysqli = null;
        if( null === $mysqli ) {
            // Connexion à la base
            $mysqli = new mysqli('localhost', 'user', 'pass', 'db');
        }
        
        
        // Exécution de la requête
        $this->result = $mysqli->query( $sql );
        assert('is_object( $this->result ); //* Erreur dans la requête SQL' );
    }
    
    // Retourne l'enregistrement courant.
    function current(){
        return $this->current;
    }

    // Retourne le numéro d'enregistrement
    // Attention, cela n'a aucun rapport avec l'indexe de votre table.
    function key() {
        return $this->key;
    }

    // Enregistrement suivant
    function next(){
        $this->current = $this->result->fetch_assoc();
        $this->key++;
    }

    // On positionne sur le premier enregistrement.
    function rewind(){
        $this->result->data_seek(0);
        $this->key = -1;
        $this->next();
    }

    // Le résultat courant est-il correcte ?
    function valid() {
        return is_array( $this->current );
    }
}

// On crée une nouvelle liste
$select = new Select( 'select ch1, ch2 from test' );

// Affichage de la liste
foreach( $select as $clef => $ligne ) {
  echo $clef, ' => ', implode(', ', $ligne ), '<br />';
}

?>

Par Frédéric Bouchery
ADAM Benjamin 2008 | Admin