PHP 5.3 : goto
28 octobre 2008 —Les exemples correspondant à ce point se trouvent dans le répertoire "goto".
Une des nouveautés introduite par PHP 5.3, et pas toujours bien acceptée semble-t-il, est l'ajout de la directive goto
.
Sommaire :
- Ajout de la directive goto
- Pas de support des goto dynamiques
- goto et blocs
- Pas de support de break vers une étiquette
Ajout de la directive goto
Le principe est le même que dans les autres langages que vous avez déjà eu l'occasion d'utiliser :
- Vous définissez une étiquette (un label) à un endroit dans votre code,
- Une étiquette se définissant en utilisant son nom, suivi du symbole '
:
'. - Le nom de l'étiquette est soumis aux mêmes contraintes que les noms de variables, sans le
$
.
- Une étiquette se définissant en utilisant son nom, suivi du symbole '
- et vous utilisez, à un autre endroit, la directive
goto
pour sauter vers cette étiquette.
Par exemple, pour définir une étiquette nommée "c
", et y sauter :
echo '<p>a</p>';
goto c;
echo '<p>b</p>';
c:
echo '<p>c</p>';
L'affichage commencera par la première instruction echo
, et l'exécution sautera ensuite vers l'étiquette c
, n'exécutant pas la seconde sortie écran :
a c
Quelle utilité pour vos programmes ?
Certains développeurs vous diront aucune
...
D'autres, peut-être un brin plus modéré, admettront l'usage de goto
dans quelques cas exceptionnels, typiquement pour sortir de profondes imbrications de boucles[1].
Pas de support des goto dynamiques
L'instruction goto
souffre tout de même d'une certaine limitation, qui ne sera peut-être que bénéfique à la qualité du code de certaines applications PHP : il n'est pas possible de sauter vers une étiquette dont le nom serait déterminé dynamiquement, à l'exécution : le nom de l'étiquette vers laquelle l'exécution de code sautera doit être inscrit en dur dans le code PHP.
Par exemple, prenons la portion de code suivante :
echo '<p>a</p>';
$label = 'c';
goto $label; // Parse error: syntax error, unexpected T_VARIABLE, expecting T_STRING
echo '<p>b</p>';
c:
echo '<p>c</p>';
Essayer d'exécuter ce code ménera à une Parse Error : le code ne compile même pas :
goto et blocs
La nouvelle instruction goto
permet de sauter en plein milieu de certains types de blocs ; typiquement, les blocs conditionnels if
.
Par exemple, prenons le code suivant :
$a = 10;
goto label;
if ($a == 20) {
label:
echo 'a == 20 !';
} else {
echo 'a != 20';
}
La sortie obtenue sera :
a == 20 !
Alors que a
ne vaut définitivement pas 20
!
(Voici typiquement un très mauvais exemple d'utilisation de goto
, soit dit en passant ^^ )
Par contre, vous ne pouvez pas sauter au milieu d'un bloc correspondant à une instruction de boucle (for
, while
, ...), ni au milieu d'un bloc switch
.
Par exemple, en utilisant le code suivant :
goto label2;
for ($i=0 ; $i<10 ; $i++) {
label2:
echo $i . "\n";
}
Vous obtiendrez une Fatal Error :
Pas de support de break vers une étiquette
Enfin, l'ajout du support de goto
et de la notion d'étiquettes aurait pu amener à penser que les instructions break
et continue
auraient pu être modifiées, de manière à être capables de sauter vers une étiquette, ce qui est tout de même plus lisible -- et surtout, plus maintenable, que de les utiliser pour sortir d'un nombre de boucles, ce qui casse à chaque fois que l'on modifie nos imbrications...
(Ceux qui ont déjà développé en Perl verront probablement de quoi je parle, à ce sujet)
Mais cette fonctionnalité n'a pas été introduite :-(
Par exemple, voici break
utilisée pour sortir d'une double imbrication :
for ($i=0 ; $i<5 ; $i++) {
for ($j=0 ; $j<5 ; $j++) {
for ($k=0 ; $k<5 ; $k++) {
echo "$i ; $j ; $k \n";
if ($k == 2) {
echo "break 2\n";
break 2;
}
}
}
}
Est-ce que ce code ne serait pas plus lisible écrit de la manière suivante :
for ($i=0 ; $i<5 ; $i++) {
label:
for ($j=0 ; $j<5 ; $j++) {
for ($k=0 ; $k<5 ; $k++) {
echo "$i ; $j ; $k \n";
if ($k == 2) {
echo "break 'label'\n";
break 'label'; // ne break que de un niveau => ne fonctionne pas
// et sans les quotes :
// Notice: Use of undefined constant label - assumed 'label'
}
}
}
}
(La sortie de cet exemple trop longue pour être reproduite ici)
Mais ce code ne fonctionne pas :
- En utilisant <<
break label;
>>, on obtient une notice :
- Et en mettant nous-même les quotes (en utilisant <<
break 'label';
>>, donc), nous ne sortons que d'un niveau de la boucle, et pas deux comme nous l'aurions souhaité.
Peut-être pour une prochaine version ?
Note
[1] Encore que, en PHP, nous avons la possibilité d'utiliser l'instruction break
en lui précisant de combien de niveaux de profondeur elle doit sortir...