foreach
. Nous allons également découvrir l’interface Iterator
et implémenter certaines de ses méthodes.
Parcourir un objet en utilisant une boucle foreach
Le PHP nous permet simplement de parcourir un objet afin d’afficher la liste de ses propriétés et leurs valeurs en utilisant une boucle foreach
.
Attention cependant : par défaut, seules les propriétés visibles (publiques) seront lues. Prenons immédiatement un exemple pour illustrer cela.
<!DOCTYPE html> <html> <head> <title>Cours PHP & MySQL</title> <meta charset="utf-8"> <link rel="stylesheet" href="cours.css"> </head> <body> <h1>Titre principal</h1> <?php //Définition d'une classe class Test{ public $publique1 = 'Variable publique 1'; public $publique2 = 'Variable publique 2'; public $publique3 = 'Variable publique 3'; protected $protegee = 'Variable protégée'; private $privee = 'Variable privée'; } //Instanciation de la classe $test = new Test(); //Parcours et affichage des propriétés visibles foreach ($test as $clef => $valeur){ echo $clef. ' => ' .$valeur. '<br>'; } ?> <p>Un paragraphe</p> </body> </html>
Ici, on crée une classe Test
qui contient cinq propriétés dont une protégée et une privée. Notez qu’ici je place tout le code sur une seule page pour plus de simplicité. Ce n’est cependant pas recommandé en pratique (rappelez-vous : une classe = un fichier).
Ensuite, on instancie la classe et on utilise une boucle foreach
sur l’objet créé afin d’afficher la liste des propriétés visibles qu’il contient et leurs valeurs associées.
Comme vous pouvez le constater, seules les propriétés publiques sont renvoyées par défaut.
On va pouvoir gérer la façon dont un objet doit être traversé ou parcouru en implémentant une interface Iterator
qui est une interface prédéfinie en PHP.
L’interface Iterator
définit des méthodes qu’on va pouvoir implémenter pour itérer des objets en interne. On va ainsi pouvoir passer en revue certaines valeurs de nos objets à des moments choisis.
L’interface Iterator
possède notamment cinq méthodes qu’il va être intéressant d’implémenter :
- La méthode
current()
; - La méthode
next()
; - La méthode
rewind()
; - La méthode
key()
; - La méthode
valid()
.
Une nouvelle fois, rappelez-vous bien ici qu’une interface n’est qu’un plan de base qui spécifie une liste de méthodes que les classes qui implémentent l’interface devront implémenter.
Les interfaces prédéfinies comme Iterator
ne servent donc qu’à produire un code plus compréhensible et plus structuré (notamment car les autres développeurs vont « reconnaitre » l’utilisation d’une interface prédéfinie et donc immédiatement comprendre ce qu’on cherche à faire).
Nous allons donc devoir ici implémenter les méthodes définies dans Iterator
dans la classe qui implémente cette interface. Bien évidemment, une nouvelle fois, on peut définir n’importe quelle implémentation pour nos méthodes mais dans ce cas utiliser une interface perd tout son sens.
Généralement, on va se baser sur le nom des méthodes pour définir une implémentation cohérente et utile. En effet, vous devez savoir que les fonctions current()
, next()
, rewind()
et key()
existent toutes déjà en tant que fonctions prédéfinies en PHP. Nous allons donc les utiliser pour définir l’implémentation de nos méthodes.
<!DOCTYPE html> <html> <head> <title>Cours PHP & MySQL</title> <meta charset="utf-8"> <link rel="stylesheet" href="cours.css"> </head> <body> <h1>Titre principal</h1> <?php //Définition d'une classe class Test implements Iterator{ private $tableau = []; public function __construct(array $tb){ $this->tableau = $tb; } public function rewind(){ echo 'Retour au début du tableau<br>'; reset($this->tableau); } public function current(){ $tableau = current($this->tableau); echo 'Elément actuel : ' .$tableau. '<br>'; return $tableau; } public function key(){ $tableau = key($this->tableau); echo 'Clef : ' .$tableau. '<br>'; return $tableau; } public function next(){ $tableau = next($this->tableau); echo 'Elément suivant : ' .$tableau. '<br>'; return $tableau; } public function valid(){ $clef = key($this->tableau); $tableau = ($clef !== NULL && $clef !== FALSE); echo 'Valide : '; var_dump($tableau); echo '<br>'; return $tableau; } } $tbtest = ['C1' => 'V1', 'C2' => 'V2', 'C3' => 'V3']; $objet = new Test($tbtest); foreach ($objet as $c => $v){ echo $c. ' => ' .$v. '<br><br>'; } ?> <p>Un paragraphe</p> </body> </html>
Ce code contient de nombreuses choses intéressantes à expliquer et qui devraient vous permettre de mieux comprendre l’orienté objet en PHP en soi.
Tout d’abord, vous devez bien comprendre qu’une méthode de classe est un élément différent d’une fonction en PHP. En effet, la fonction current()
par exemple est une fonction prédéfinie (ou prête à l’emploi) en PHP et on ne peut donc pas la redéfinir.
En revanche, la méthode current()
n’est pas prédéfinie et on va donc pouvoir définir son implémentation. Ici, en l’occurrence, on utilise la fonction prédéfinie current()
pour implémenter la méthode current()
.
Notre classe Test
implémente l’interface Iterator
. Elle possède ici une propriété privée $tableau
qui est un tableau vide au départ et définit un constructeur qui accepte un tableau en argument et le place dans la propriété privée $tableau
. Ensuite, la classe se contente d’implémenter les méthodes de l’interface Iterator
.
Ensuite, je pense qu’il convient d’expliquer ce que font les fonctions prédéfinies current()
, next()
, rewind()
et key()
pour comprendre le code ci-dessus.
La fonction reset()
replace le pointeur interne au début du tableau et retourne la valeur du premier élément du tableau (avec une instruction de type return
).
La fonction current()
retourne la valeur de l’élément courant du tableau, c’est-à-dire de l’élément actuellement parcouru (l’élément au niveau duquel est situé le pointeur interne du tableau).
La fonction key()
retourne la clef liée à la valeur de l’élément courant du tableau.
La fonction next()
avance le pointeur interne d’un tableau d’un élément et retourne la valeur de l’élément au niveau duquel se situe le pointeur.
Une fois notre classe définie, on l’instancie en lui passant un tableau associatif qui va être utilisé comme argument pour notre constructeur.
Finalement, on utilise une boucle foreach
pour parcourir l’objet créé à partir de notre classe.
Ce qu’il faut alors bien comprendre ici est que le PHP a un comportement bien défini lorsqu’on utilise un objet qui implémente l’interface Iterator
et notamment lorsqu’on essaie de le parcourir avec une boucle foreach
.
Notez par ailleurs que la plupart des langages web ont des comportements prédéfinis lorsqu’on utilise des éléments prédéfinis du langage et c’est généralement tout l’intérêt d’utiliser ces éléments.
Expliquons précisément ce qu’il se passe dans le cas présent. Tout d’abord, on sait qu’une interface impose aux classes qui l’implémentent d’implémenter toutes les méthodes de l’interface. Lorsqu’on crée un objet qui implémente l’interface Iterator
, le PHP sait donc que l’objet va posséder des méthodes rewind()
, current()
, key()
, next()
et valid()
et il va donc pouvoir les exécuter selon un ordre prédéfini.
Lorsqu’on utilise une boucle foreach
avec un objet qui implémente l’interface Iterator
, le PHP va automatiquement commencer par appeler Iterator::rewind()
avant le premier passage dans la boucle ce qui va dans notre cas echo
le texte « Retour au début du tableau » et va placer le pointeur interne du tableau au début de celui-ci.
Ensuite, avant chaque nouveau passage dans la boucle, Iterator::valid()
est appelée et si false
est retourné, on sort de la boucle. Dans le cas contraire, Iterator::current()
et Iterator::key()
sont appelées.
Finalement, après chaque passage dans la boucle, Iterator::next()
est appelée et on recommence l’appel aux mêmes méthodes dans le même ordre (excepté pour rewind()
qui n’est appelée qu’une fois en tout début de boucle).