Cette page offre un lieu de discussion qui vise à formaliser nos Normes Et Recommandations De Developpement, sur l'aspect particulier du code PHP.
Candidats à la norme
Nous listons ici les items qui ont l'air de faire consensus et sont donc candidats à intégrer les normes de codage PHP pour
WikiNi.
Il existe des règles indispensables pour des raisons de compatibilité ou autre, et des recommandations. Les recommandations sont fortement conseillées : tout développeur peut, sans discussion, les appliquer
a posteriori sur un code qui ne les respecte pas.
Règles indispensables
- Peu importe les règles du moment qu'elles soient appliquées par tout le monde ; c'est surtout l'homogénéité que nous visons ici, car elle seule garanti les qualités de lisibilité, propreté du code, etc.
- Les règles ne sont pas remises en cause une fois stabilisés et versionnées : si l'on veut changer de règles, il faut alors produire une autre version des règles.
- Utilisation des short-tags "<?" interdite. Toujours utiliser "<?php". Cette règle assure une meilleure compatibilité de WikiNi avec tous les hébergeurs, certains n'acceptant pas les short-tags.
Recommandations
- Aucun espace ne précède une virgule ou un point-virgule
- Une virgule ou un point-virgule est toujours suivi d'un espace, ex:
- $pos = strpos('u', 'bonjour à tous', 10);
- for($i = 0; $i < 10; $i++) {
- }
- Après un point-virgule, on revient toujours à la ligne (sauf dans les for bien entendu). Il ne peut donc y avoir qu'une seule instruction par ligne.
- La dernière instruction avant un tag de fermeture php ("?>") est tout de même suivie d'un point-virgule.
- Les constantes doivent être en majuscules, avec des traits de soulignement ("underscore": "_") pour séparer les mots
- Quid des constantes true, false et null ? Personnellement je préfère les mettre en minuscules. -- LordFarquaad
- Moi je veux bien mais il faudrait éviter les cas particuliers... -- CharlesNepote
- Certes mais quand on parle ici de majuscules, c'est plutôt pour la façon de nommer les nouvelles constantes. J'imagine que la plupart des IDE mettent en évidence ces trois constantes "spéciales", quelle que soit leur casse. -- LordFarquaad
Je propose de spécifier ici aussi qu'il est recommandé (je dirais même plus: indispensable !) de documenter toute portion de code, en respectant le modèle de phpDucumentor. --
LordFarquaad
Point de divergences
Formatage des blocs
Ce problème est assez important car :
- il a un fort impact sur la lisibilité du code
- il détermine, au premier coup d'oeil, la propreté du code et augure donc de la qualité du produit
1. Les blocs doivent être ouverts sur la même ligne. Inutile de mettre une nouvelle ligne avec juste pour une accolade ou une parenthèse ouvrante... (
indent -br -ce)
- if ($a) {
- } else { // Où met-je mon commentaire ?
- }
ou 2. Les blocs sont ouverts sur une autre ligne (
indent -bl -bli0 -nce) :
- // Dans tel cas on fait ceci
- if ($a)
- {
- }
- // Et dans tel autre on fait cela
- else
- {
- }
ou 3. (
indent -br -nce) :
- // Dans tel cas on fait ceci
- if ($a) {
- }
- // Et dans tel autre on fait cela
- else {
- }
Pour résoudre ce problèmes on peut voir :
- ce que proposent les éditeurs par défaut
- PhpEdit? propose par défaut des keyboard templates correspondant à la syntaxe 1 ou 3. (aucune des deux en particulier puisqu'il ne propose rien pour les if... else par défaut)
- ce que proposent les enjoliveurs de code par défaut
- ce que proposent les autres normes
- les raisons invoquées par les développeurs :
- pour ma part je trouve beaucoup plus lisible la solution 2 : pour moi il n'y a pas photo : les blocs se découpent mieux et sont plus facile à repérer visuellement dans ce cas -- CharlesNepote
- pour ma part je préfère largement la solution 1, qui me semble tout aussi lisible une fois qu'on y est habitué, car la solution 2 "gaspille" trop de lignes à l'affichage et rend la lecture du code difficile quand il contient de nombreuses conditionnelles ; mais le plus important est surtout de faire un choix et de s'y tenir globalement, peu importe lequel en ce qui me concerne -- ProgFou
- donc c'est une question de goût et il semble que vous soyez deux avec LordFarquaad à préférer la 1 ; je d'accord sur le fait que peu importe le standard du moment qu'on adopte tous le même -- CharlesNepote
- Oui je préfère aussi la 1, cependant le code que j'ai écrit pour l'ActionInclude me fait un peu hésiter: ça devient un peu lourd quand il y a beaucoup de elseif... Par contre la 3, qui est une sorte de compromis, a pour moi tout de même quelques désavantages:
<?php
// lisibilité quand il y a deux if d'affilée:
if ( $a ) {
echo $b;
}
else {
echo $c;
}
if ( $d ) { // l'indépendance entre ce if et le précédent ne me parait pas claire, devrait-on placer un interligne avant ?
echo $e;
}
// que deviennent les do... while ? L'homogénéité voudrait ceci:
do {
echo $a
}
while ( $i-- ); // le while me parait trop détaché, on pourrait croire qu'il s'agit d'une simple boucle séparée...
?>
Etant donné qu'il y a rarement beaucoup de elseif, je reste plutôt sur mon premier avis -- LordFarquaad
En fait, la 1 ne me convient vraiment pas pour les raisons suivantes :
- il est difficile de placer un commentaire pour le bloc else à l'inverse des solutions 2 et 3
- elle perturbe les fonctions de pliage/dépliage du code dans certains logiciels (Scite), à l'inverse des solutions 2 et 3
- elle perturbe les fonctions de mise en valeur des blocs dans certains logiciels (Scite), à l'inverse des solutions 2 et 3
Je suis d'accord sur la 3 même si je préfère toujours la 2.
Exemples montrant pourquoi j'aime bien 2 :
<?php
// underlinue
else if ($thing == "__")
{
static $underline = 0;
return (++$underline % 2 ? "<u>" : "</u>");
}
// monospace
else if ($thing == "##")
{
static $monospace = 0;
return (++$monospace % 2 ? "<tt>" : "</tt>");
}
// Deleted
else if ($thing == "@@")
{
static $deleted = 0;
return (++$deleted % 2 ? "<span class=\"del\">" : "</span>");
}
// Inserted
else if ($thing == "££")
{
static $inserted = 0;
return (++$inserted % 2 ? "<span class=\"add\">" : "</span>");
}
?>
--
CharlesNepote
Pour les commentaires il y a toujours moyen de les placer à l'intérieur, je n'aime pas trop de détacher un
else de son
if, même par un commentaire...
Donc pour le code que tu donnes, ça donnerait (même si l'idéal serait d'utiliser un switch tout de même...):
<?php
elseif ($thing == "__") { // commentaire ici
static $underline = 0;
return (++$underline % 2 ? "<u>" : "</u>");
} elseif ($thing == "##") {
// ou alors là
static $monospace = 0;
return (++$monospace % 2 ? "<tt>" : "</tt>");
} elseif ($thing == "@@") {
/*
* Ou même comme ceci,
* en plusieurs lignes
*/
static $deleted = 0;
return (++$deleted % 2 ? "<span class=\"del\">" : "</span>");
} elseif ($thing == "££") { // Inserted
static $inserted = 0;
return (++$inserted % 2 ? "<span class=\"add\">" : "</span>");
}
?>
Par contre pour les deux problèmes que tu mets en avant au sujet de certains logiciels, j'imagine qu'ils sont inévitables... (du moins pour le pliage/dépliage) Est-ce si grave ?
--
LordFarquaad
Franchement, quand on regarde les deux fenêtres ci-dessus, je trouve vraiment qu'il n'y a pas photo ! La première fenêtre contient un code très lisible où chaque bloc est clairement séparé, un commentaire annonçant clairement le contenu de chaque bloc. La deuxième me paraît très fouillie et inélégante. D'autres auteurs vont dans mon sens :
Either form is acceptable, many people, however, find the first form more pleasant. Why is the topic of many psychological studies.
There are more reasons than psychological for preferring the first style. If you use an editor (such as vi) that supports brace matching, the first is a much better style. Why? Let's say you have a large block of code and want to know where the block ends. You move to the first brace hit a key and the editor finds the matching brace. (
source).
Hormis l'argument des outils gérant mieux les solutions 2 et 3, il y a aussi le poids de l'existant : je ne vois nulle part dans
WikiNi l'utilisation de la solution 1 : il y aurrait beaucoup de code à refaire à la main puisqu'il faudrait déplacer beaucoup de commentaires. Ce qui me conduit imanquablement au troisième argument massue ;) : les solutions 2 et 3 se prettent bien à l'indentation automatique alors que cela devient impossible dans le cas de la solution 1 (à cause des commentaires). A la rigueur je veux bien accepter la 3. Cela dit, je m'inclinerai s'il existe des contre-arguments et une majorité pro-1. --
CharlesNepote
Pour moi les deux exemples sont aussi clairs l'un que l'autre, sauf que le deuxième est beaucoup plus court pour faire la même chose...
Je ne comprends pas trop l'histoire des accolades correspondantes: trouver l'accolade fermante d'une ouvrante n'est pas un question d'indentation mais de comptage correct des accolades imbriquées :-s D'ailleurs la plupart des ide repèrent correctement cela, quelque soit l'indentation...
Enfin, apparemment comme tu dis beaucoup d'outils gèrent mieux les solutions 2 et 3 (le CodeBeautifier de
PhpEdit? ne semble pourtant gérer que les 1 et 2 lui tiens) ce qui me parait en effet être un gros avantage pour que la norme ne soit pas (trop) contraignante.
Par contre pour ce qui est de l'utilisation des diverses solutions dans
WikiNi, c'est pas tout à fait vrai ce que tu dis... La solution 2 semble certes beaucoup utilisée, mais rien que dans wakka.php on trouve un peu de tout:
- des déclarations de fonctions sur une seule ligne, totalement illisibles (la pire étant LoadWantedPages qui, chez moi [en 1280*1024], prend plus de 3 écrans de large...)
- des déclarations de fonctions des types 1 ou 3 (SavePage, PurgePages...)
- une majorité de déclarations de type 2
- des instructions conditionnelles de type 1 (GetInterwikiUrl, // check for locking) mais apparemment aucune du type 3 (la plupart du temps c'est écrit en ligne de façon très illisible, et il n'y a d'ailleurs pas d'accolades)
Quand tu dis que les solutions 2 et 3 se prettent mieux à l'indentation automatique à cause des commentaires, tu veux dire pour passer de l'une à l'autre ?
Finalement, vu les arguments que tu donnes (et que la 3 ne semble pas gérée par phpedit), je suis d'accord pour la 2. Reste à voir ce que
les autres en pensent. L'important pour moi c'est de décider rapidement d'un bon standard commun, afin que les prochaines développements le respectent directement. Ceci est d'ailleurs valable également pour la documentation... (c'est vraiment dommage que nous ne soyons que trois à participer à cette discussion qui est tout de même à l'échelle de tous les développeurs, y compris ceux qui ne font pas partie de l'équipe de développement mais voulant voir leurs contributions intégrées à
WikiNi...) --
LordFarquaad
Finalement j'adopte le modèle n°2. Au début ça fait bizarre mais on finit par en apprécier rapidement la clarté. Il faudra juste déterminer quelle présentation il faut utiliser pour les commentaires desinstructions condiftionnelles et des boucles (avant le
if, else etc. / en fin de ligne, / entre le
if, else etc. et l'accolade ouvrante / sur la même ligne que l'accolade ouvrante...)
Je pense qu'on peut commencer à rédiger une page
NormeDeCodagePHPPourWikiNi? avec les résultats des discussions, afin d'y voir un peu plus clair là-dedans. --
LordFarquaad
Notes :
On peut automatiser l'indentation d'un fichier avec la commande
indent sous GNU/Linux.
Je propose d'utiliser les options de
indent pour spécifier notre norme de façon rapide et non-ambiguë.
Voir aussi
le manuel de indent et
indent pour Windows.
--
ProgFou
Sur le principe, je suis d'accord pour utiliser un automatisme pour indenter. Quelques remarques :
- tout le monde n'aura peut-être pas la facilité d'utiliser simplement cet outil (indent a une installation camboui sous win32, mon IDE le fait déjà alors pourquoi me rajouter un outil ?, etc.)
- Pour ce problème je pensais justement créer une mini-interface web d'appel à indent (sur mon serveur ou ailleurs) pour faciliter les choses, via un formulaire web (textarea) par exemple. -- ProgFou
- cette tâche peut éventuellement être réalisée ponctuellement : manuellement, par un ou plusieurs développeurs à chaque commit du CVS, ou bien automatiquement avant chaque publication de WikiNi
- indent semble destiné au C : fonctionne-t-il aussi bien pour le PHP ?
- Il n'y a pas tellement de différence syntaxique entre le C et le PHP, donc oui, ÀMHA. À tester de toutes façons. -- ProgFou
- Et que va-t-il se passer lorsqu'il va trouver du HTML ? -- CharlesNepote
--
CharlesNepote
Commentaires
Personnellement j'aime bien de les mettre à l'intérieur des instructions conditionnelles, et précéder celles-ci d'une explication de ce qu'on veut faire, par exemple (je me force pour les blocs lol):
<?php
// vérifions le nombre de pattes de notre animal
if($nbDePattes > 8)
{ // mille-pattes
echo 'Cet animal est un myriapodes';
}
elseif ($nbDePattes == 8)
{ // arraignée, crustacé
echo 'Il s\'agit d\'une arraignée ou d\'un crustacé';
}
elseif ($nbDePattes == 6)
{ // insecte
echo 'Ceci est un insecte';
}
elseif ($nbDePattes == 4)
{ // mammifères, reptiles
echo 'Un animal à quatre pattes, probablement un mammifères ou un reptile';
}
elseif ($nbDePattes == 2)
{ // singes, oiseaux... humains
// a-t-il des ailes ?
if (nbAiles)
{ // oiseaux
echo 'C\'est un oiseau';
}
else
{ // autres
echo 'Ceci est un humain ou un singe';
}
}
elseif (!$nbDePattes)
{ // rampant, poisson
echo 'Ceci est un animal rampant ou alors un poisson';
}
else
{ // 1, 3, 5 ou 7 pattes
echo 'Mmm un animal très étrange qui provient certainement de p3x972 !';
}
?>
Bon évidemment c'est un exemple simpliste où les commentaires sont un peu redondants, mais je trouve plus logique de dire d'abord ce qu'on veut faire ou vérifier, et ensuite que les descriptions des différents cas se fassent là où ces cas sont rencontrés. En mettant un commentaire juste avant un
else, il se retrouve placé à la fin d'un
if (ou
elseif), c'est à dire à un point où aucune instruction ne peut se trouver, donc en quelque sorte il commente un point inatteignable (même si on le comprend) et marque un détachement entre les
if et les
else que je trouve assez perturbant. Ceci a certes moins d'importance à mes yeux que la structure des blocs de code mais je pense qu'il faudrait tout de même officialiser une norme quant aux commentaires. (notemment les met-on sur la même ligne que l'instruction, ou sur la ligne qui précède ?)
Il me semble qu'actuellement les commentaires précédènt en général ce à quoi ils s'appliquent, que ce soit des instructions, des conditions, des boucles ou les différents cas d'un
switch. Pour moi ça peut être conservé ainsi, même si je préfère faire autrement dans le cas des
if-elseif-else... --
LordFarquaad
Documentation
La discussion a été lancée dans
ModeleObjetDeWikiNi. Pour moi le modèle des phpDocumentor est vraiment très bon et tend à se standardiser, je propose qu'on se mette à l'appliquer dès à présent. --
LordFarquaad
Préférences de chaque développeur
Pour mémoire, j'utilise comme éditeur : principalement Scite (Win et Linux), parfois Jedit, parfois Dreamweaver, parfois Kate et je découvre PHPEclipse qui m'a l'air très prometteur.
Pour ma part, j'apprécie :
- l'usage des tabulations plutôt que des espaces (plus faciles à utiliser dans n'importe quel éditeur ; consomment moins de place)
- l'usage du renvoi systématique à la ligne des parenthèses (quelque soit leur usage : fonctions, boucles, conditions, etc.) : je trouve le code beaucoup plus lisible comme cela et mon éditeur (Scite) me fait apparaître correctement ces blocs
- l'usage des retours de lignes ("\n") pour avoir un code HTML généré lisible : bien plus facile pour déboguer ; par exemple : echo "<h1>Mon code HTML</h1>\n";
- a priori je tolère la notation sur une seule ligne d'une structure conditionnelle si elle ne possède qu'une seule instruction :
- if ($b) echo $b;
- je n'aime pas if ($b) echo $b; print_r ($array); ; plus difficile à lire
- j'ai découvert récemment les vertus des lignes de tirets pour structurer du code (je ne l'utilise pas dans WikiNi) (65 caractères de long)
- je saute trois lignes vides entre les parties structurellement différentes (il ne devrait pas y avoir plus de 3 ou 4 parties de ce type)
- je me tâte pour l'usage des espaces
<?php
// -- Traitements --------------------------------------------
//
// [du code]
// -- Présentation -------------------------------------------
//
echo "<h1>Mon code HTML</h1>\n";
if (isset($_REQUEST['test'])
{
echo "ok\n";
}
else
{
echo "zut\n";
}
// -- Partie relative aux fonctions ----------------------------
//
function TestTest ($tutu, $toto)
{
// du code
echo "<a href=\"".$url."\">".$url."</a>".$matches[2];
//
echo "<a href=\"" . $url . "\">" . $url . "</a>" . $matches[2];
}
Evidemment, je suis ouvert à toute autre norme. N'hésitez pas à indiquer vos préférences en expliquant. On essaiera de dégager un consensus...
--
CharlesNepote
Plutôt que de dire ce qui me plait ou non dans ta syntaxe, je vais détailler la mienne, comme ça on pourra plus facilement comparer après:
- Limiter la longueur des lignes : toute ligne devrait apparaître entièrement avec une résolution de 1024 en largeur et des tailles de police "normales". (Ceci doit faire à peu près 100 à 120 carractères)
- Aucun espace ne précède une virgule ou un point-virgule
- Une virgule ou un point-virgule est toujours suivi d'un espace, ex:
- $pos = strpos('u', 'bonjour à tous, 10);
- for($i = 0; $i < 10; $i++) {
- }
- Après un point-virgule, on revient toujours à la ligne (sauf dans les for bien entendu). Il ne peut donc y avoir qu'une seule instruction par ligne.
- La dernière instruction avant un tag de fermeture php ("?>") est tout de même suivie d'un point-virgule.
- Attachement des parenthèses ouvrantes / instruction de bloc conditionnel ou nom de fonction:
- if(...)
- while(...)
- function MaFonction(...)
- MaFonction(...);
- Détachement des accolades:
- do {
- } while($i < 10);
- if($i > 10) {
- echo 'Désolé d'avoir affiché "$i" alors que c'était supérieur ou égal à 10...'
- } elseif($i < 10) {
- echo 'Apparemment php ne fonctionne pas bien car "$i" est inférieur à 10';
- } else {
- echo 'Tout s'est passé comme voulu, c\'est chouette non ?';
- }
- Détachement des opérateurs / opérandes ou parenthèses
- Valable aussi lors de la concaténation (recollage) de deux chaines, le point doit être prévédé et suivi d'un seul espace: $a . $b
- Attachement des opérandes / parenthèses (j'avoue que je ne respecte pas parfaitement cette règle...):
- Utiliser dès que c'est possible les opérateurs combinés pour l'assignement: .=, +=, -=, *=, /=, %=, &= etc. (voir et notemment un des commentaires en bas de page)
- Les blocs doivent être indentés, et ce avec des tabulations. Dans le cas d'un switch, tout doit être indenté:
- switch($maVar) {
- case 'show':
- case 'none':
- default:
- echo 'Une erreur s'est produite, veuillez recommencer';
- }
- Les blocs doivent être ouverts sur la même ligne. Inutile de mettre une nouvelle ligne avec juste pour une accolade ou une parenthèse ouvrante...
- Utilisation des short-tags "<?" ou "<?=" interdite. Toujours utiliser "<?php" ou "<?php echo ".
- Eviter au maximum de mélanger traitement et affichage, et surtout éviter au maximum d'ouvrir et fermer les tags php de multiples fois. Un très bon exemple de ce que je n'aime pas du tout est l'action header de WikiNi...
- Il ne peut y avoir d'instruction sur la même ligne qu'une tag php ouvrante ou fermante, mis à part pour une instruction d'affichage en ligne du type
- Pour l'affichage, utiliser "echo" sans parenthèses et recoller toutes les chaines avant de les passer à "echo" (à moins que ce ne soit moins performant ?), c'est à dire utiliser:
- plutôt que:
- qui me parait moins lisible.
- Oui, c'est moins performant car avec `.', le moteur PHP fait une concaténation inutile, alors qu'avec `,', il ne fait qu'afficher à la suite. Bien sûr cela se compte en centième de seconde. ;) -- MickaelMenu
- Les structures conditionnelles peuvent être notées sur une seule ligne, mais elles doivent respecter les points suivants:
- L'inscrution en elle-même ne doit pas être trop longue (disons maximum 40 à 50 carractères)
- La règle 1...
- Il doit y avoir un espace après la parenthèse fermente de la structure conditionnelle.
- Elle ne peut pas être mélangée avec la synthaxe en bloc, par exemple comme ceci:
- if($a) { echo $b; } // mauvais
- if($a) echo $b; // bon
- L'utilisation d'un for ne contenant aucune instruction est autorisée. Dans ce cas il faut tout de même mettre un espace entre la parenthèse fermante et le point-virgule, pour faire resortir celui-ci:
- $result = mysql_query('SELECT * FROM matable');
- for($table = array(); $ligne = mysql_fetch_assoc($result); $table[] = $ligne) ;
- Les noms de fonctions, classes et méthodes sont composés de mots dont la première lettre est une majuscule et les autres sont des minuscules. (ceci en fait d'ailleurs des NomWiki...). L'utilisation du chiffre "2" est autorisée pour les fonctions de conversion, marquant ainsi une séparation nette entre l'état en entrée et l'état en sortie. Par exemple nl2br. Elles ne comportent pas d'underscore.
- Les noms des variables sont similaires aux noms au noms des fonctions, sauf que le premier mot commence par une minuscule.
--
LordFarquaad