PHP 5.3 : goto

28 octobre 2008php, php-5.3

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

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 $.
  • 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 :

not-goto-dynamic-label.png


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 :

not-goto-into-loop.png


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 :

not-break-label.png

  • 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…

Vous avez apprécié cet article ? Faites-le savoir !

Ce blog a récemment été migré vers un générateur de sites statiques et je n'ai pas encore eu le temps de remettre un mécanisme de commentaires en place.

Avec un peu de chance, je parviendrai à m'en occuper d'ici quelques semaines ;-)