Si vous êtes versés dans le monde PHP, il y a quelques chances que vous ayez entendu parler de la norme PSR-0. Par contre, les normes PSR-1 et PSR-2 sont plus récentes, et de fait moins connues.
Ces normes sont définies par le « PHP Framework Interoperability Group » (FIG en abrégé), un groupe d’acteurs connus du monde PHP, contributeurs de frameworks, ORM et bibliothèques parmi les plus connus. Tout ce petit monde s’est réuni pour tenter de définir un ensemble de règles communes, afin de faciliter l’utilisation de différentes briques logicielles écrites en PHP.
L’effort est louable. La norme PSR-0 était absolument nécessaire, mais correspondait plutôt à la normalisation d’une bonne pratique répandue. La norme PSR-1 me semble personnellement pleine de bon sens.
Par contre, quand j’ai lu le contenu de la norme PSR-2, je me suis dit qu’ils avaient fait fausse route. Je sais bien qu’il faut parfois faire certains choix. Mais après avoir exposé les choses à mes développeurs, je me suis décidé à écrire un article sur le sujet.
PSR-0
La norme PSR-0 a pour but de faciliter le chargement automatique d’objets (aussi connu sous le nom d’autoload), par la mise en place d’espaces de nommage correspondant aux arborescences de fichiers sur disque. Le tout est très cohérent et très utile.
Comme je le disais plus haut, PSR-0 formalise une bonne pratique qui était déjà assez répandue.
PSR-1
La norme PSR-1 se repose sur PSR-0, et y ajoute quelques détails qui ont leur importance :
- Seuls les tags PHP <?php et <?= sont acceptés.
- Le code PHP doit être encodé en UTF-8.
- Les fichiers PHP peuvent contenir de la déclaration de symboles ou produire des écritures, mais pas les deux à la fois.
- Les noms de classes doivent être écrits en StudlyCaps.
- Les constantes s’écrivent en majuscule.
- Les méthodes s’écrivent en camelCase.
Encore une fois, tout cela n’est que du bon sens, et était déjà largement utilisé.
La seule chose qui manque, à mon avis, concerne les attributs de classe : leur écriture est volontairement laissée à l’appréciation de chacun. J’aurais personnellement préféré les normaliser en camelCase.
PSR-2
PSR-2 est une norme qui concerne le style d’écriture du code. Je vous laisse lire son contenu, mais moi ça m’a fait le même effet qu’à Brian Moon, qui a écrit un très bon article sur le sujet. Je vous invite aussi à lire son article, mais tout se résume avec une image :
http://xkcd.com/927/
Je trouve cette image savoureuse. C’est tellement vrai, non ? :-)
Bon, mais pourquoi elle me défrise, cette norme PSR-2 ? Contrairement à ce que dit Brian Moon dans son article, je ne pense pas qu’il soit complètement vain de vouloir encore normaliser le style d’écriture du code ; c’est une chose importante, qui mérite qu’on s’y attarde. C’est juste que je pense sincèrement qu’ils auraient pu faire des choix plus judicieux.
En l’occurrence, voici les points qui me dérangent :
- L’indentation du code se faire avec 4 espaces, pas avec des indentations.
- Les objets et les méthodes ont leur accolade ouvrante sur la ligne d’après.
- Les structures de contrôle ont leur accolade ouvrante sur la même ligne.
- Sur les méthodes, le mot-clé static doit être utilisé après la visibilité.
Je vais revenir sur les deux premiers points.
Je déteste la non-consistance entre le deuxième point et le troisième. Pourquoi l’accolade ouvrante n’est pas toujours sur la même ligne (ou, à la limite, pourquoi y serait-elle jamais) ? À mon sens, ce n’est pas cohérent ; et j’ai du mal à voir la logique qui peut conduire à ce choix. On peut argumenter que les objets et les méthodes sont « spéciaux », qu’ils sont différents d’un if ou d’un while… mais je répondrais “Et alors ?”.
Pour le dernier point, c’est encore un problème de consistance. La norme PSR-2 dit que tous les attributs et toutes les méthodes doivent avoir une visibilité explicitement déclarée ; c’est une bonne chose. Elle dit aussi que les mots-clés abstract et final doivent être placés avant la visibilité. Je pense que c’est aussi une bonne chose ; quand on lit du code, le fait qu’une méthode soit abstraite ou finale est une information plus importante que sa visibilité ; cela a (encore) plus d’effet sur la manière dont on va l’appréhender et l’utiliser.
Mais de la même manière, je considère que le fait qu’une méthode soit statique est plus important que sa visibilité. Suivant qu’une méthode soit statique ou non, on ne l’utilisera pas du tout de la même manière. Donc il me semble logique que cette information soit placée avant la visibilité.
J’ai du mal à comprendre l’origine de cette inconsistance entre abstract, final et static. Et, entre-nous, j’ai un peu l’impression que la réponse est “Parce que c’est l’habitude en Java”…
Un peu d’histoire des normes
Je vais maintenant revenir sur les deux premier points : l’indentation à 4 espaces et les accolades sur une ligne seule.
Pour expliquer ce choix, il faut revenir en arrière et faire un petit historique des normes de codage. Vous m’excuserez, il est forcément incomplet, mais mon but est d’illustrer les deux grands types de norme qui existent.
Les GNU Coding Standards réclament l’indentation sur 2 espaces, avec les accolades à la ligne. Dans un style qui peut sembler particulier à certains, ce standard demande à indenter les accolades, ainsi que leur contenu. Ce qui donne ce genre de choses :
int ma_fonction(int param1, int param2) { if (param1 < 0) return param2; else { while (param1--) { printf("Loop : %d\n", param1); param2++; } return param2; } }
(ne cherchez pas à comprendre le code, j’ai écrit n’importe quoi)
Maintenant, intéressons-nous au Linux Kernel Coding Style. Dans le style provocateur qui caractérisait Linus Torvalds dans sa jeunesse, il y est écrit qu’il faut imprimer la norme GNU, ne pas la lire, et la brûler. Bref, la norme Linux est bien différente : l’indentation se fait avec des tabulations (dont la largeur d’affichage est réglée à 8 caractères) et les accolades sont sur la même ligne. Bizarrement, Linus considère lui aussi que les fonctions sont spéciales, et donc nécessitent d’avoir leur accolade à la ligne…
Bref, le résultat ressemble à ça :
int ma_fonction(int param1, int param2) { if (param1 < 0) return param2; else { while (param1--) { printf("Loop : %d\n", param1); param2++; } return param2; } }
Ça a un look légèrement différent, hein ?
Si on prend un peu de hauteur (intellectuellement parlant), ces deux normes utilisent des moyens différents pour atteindre le même but : améliorer la lisibilité du code en séparant visuellement les différents niveaux d’imbrication.
Pour le GNU, cela se fait en ajoutant de l’espace vertical (les accolades ouvrantes seules sur une ligne), ce qui permet d’utiliser moins d’espace horizontal.
Pour Linus, cela se fait en ajoutant de l’espace horizontal, ce qui permet d’être moins gourmant verticalement.
En fait, on peut remarquer un truc : Ceux qui utilisent Emacs utilisent la norme GNU, ceux qui utilisent vi/vim utilisent la norme Linux. Je le sais pour être passé de l’un à l’autre durant ma carrière. Ça se fait naturellement ; par défaut, indenter avec des espaces est chiant avec vi, indenter avec des tabulations n’est pas très pratique avec emacs.
Aucune coïncidence là-dedans : Emacs est un projet GNU ; Linus utilise vi.
Donc, dans ces deux grandes familles, le FIG a plus-ou-moins choisi la voie donnée par le GNU. On peut juste remarquer que mélanger un faible espacement horizontal (indentation sur 4 espaces) avec un faible espacement vertical (l’accolade ouvrante est sur la même ligne que sa structure de contrôle) est une hérésie ; c’est la double-peine, et on fini par avoir du code franchement pas facile à lire. Autant rester sur un choix clair : soit on sépare verticalement, soit on sépare horizontalement.
Comme je l’ai déjà dit, l’important avec les normes de codage, c’est d’en choisir une et de s’y tenir. Alors pourquoi est-ce que je pense que la philosophie de la norme Linux est meilleure que celle de GNU ?
Relisez ce que j’ai écrit plus haut. GNU => consomme de l’espace vertical. Linux => consomme de l’espace horizontal. Quelle est la forme de nos écrans ? Eh oui, on a de l’espace à gogo en largeur, par contre c’est en hauteur que l’espace est compté.
Voilà pourquoi je préfère écrire du code dont les blocs sont clairement séparés par des belles tabulations. Je cherche à économiser l’espace vertical plus que l’espace horizontal.
Pas vous ?
Au fait
Je rappelle toutefois que toute cette longue déblatération est un peu inutile : Il existe des plugins (pour Eclipse, par exemple) permettant de modifier la norme d’écriture d’un fichier de code… Mais autant faire les choses proprement dès le début.
Sinon, la norme PSR-2 apporte quand même des indications qui me semblent pleines de bon sens. Un espace entre les structures de contrôle et leur parenthèse ouvrante. Pas d’espace après une parenthèse ouvrante, ni avant une parenthèse fermante. Pas d’espace entre le nom d’une fonction et sa parenthèse ouvrante.