Consignes de Codage V1.1 gNico compliant 1.0


Sommaire
Consignes générales
Packages
Normalisation
Tags PHP
Structures de Contrôles
Les "Magic Numbers"
Appels de Fonction
Définitions des Fonctions
Commentaires
Inclure du Code
Indentation
Commentaires d'En-tête
Exemple d'URLs
Conventions de Nom
Derniers conseils importants
Sources

        Note du TT3: Ce document est issu de plusieurs sources (voir en bas de page), puis largement complété par nos soins. Nous ne sommes pas les auteurs du document dans son ensemble.

        Ces recommandations de codage PHP sont reconnues pour leur utilité et mises en oeuvre dans les développements professionnels de grande envergure.

Nota: n'hésitez pas à nous faire parvenir vos remarques et suggestions d'amélioration à gjouanno, gmallet ou lfallet.

Version 1.1 du 20 Mai 2003 - TT3


Consignes générales

  1. Les couches "présentation" (HTML) et "codage" (PHP) du projet devront être parfaitement séparées (l’utilisation de PHP dans les pages HTML devra être réduit à de simples appels de fonctions).
  2. Dans cette optique, les groupes possédant des fonctionnalités similaires devront collaborer pour la mise en place de classes et de librairies de fonctions communes.
  3. Toute fonction ou groupe de fonctions rendu pour être intégré au projet devra être accompagné de la documentation correspondante et, dans la mesure du possible, des fonctionnalités de tests associées.

Packages

Pour notre projet, dans la suite de ce document, sera considéré comme "package" l'ensemble des classes et librairies de fonctions remis par un même groupe. Les noms de ces packages seront donc les suivants :

- Exercice
- Sequence
- Document
- Etape
- Carte

- Lien
- Attribut
- Groupe
- Utilisateur
- Requete_bd
(méthodes d'accès à la BD)


Normalisation

Notre projet est destiné à être portable sur diverses stations de travail. Il doit donc se conformer à la norme XHTML 1.0 Transitionnal. Voici l'en-tête d'un fichier XHTML 1.0 :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

La majorité des pages seront réalisées en PHP. Leur extension sera toujours .php, et elles comporteront donc l'en-tête suivant :

<?php echo "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?".">"; ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Il va de soi qu'il est interdit d'insérer le moindre script Javascript. Le DHTML est également déconseillé. PHP et Actionscript acceptés (heureusement). Les fichiers *.inc ne contiennent aucune procédure d'affichage!

Pour vérifier aisément que votre page est conforme à la W3C, vous pouvez la valider ou insérer cette image (Il suffit ensuite de cliquer dessus dans un navigateur pour être dirigé vers la validation de votre page) :

<p><a href="http://validator.w3.org/check/referer">
   <img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!"
    width="88" height="31" border="0" align="right"/></a>
</p>

Tags PHP

Toujours utiliser <?php ?> pour délimiter du code PHP, et non la version abrégée <? ?>. Cela est obligatoire pour être conforme aux règles de PEAR et c'est aussi la méthode la plus portable pour inclure du code PHP sur différents systèmes d'exploitations et configurations.


Structures de Contrôles

Les structures de contrôles incluent les 'if', 'for', 'while', 'switch', etc. Vous trouverez ici un exemple de structure 'if' qui est la plus compliquée :

if ((condition1) || (condition2)) {
    action1;
}
elseif ((condition3) && (condition4)) {
    action2;
}
else {
    defaultaction;
}

Les instructions de contrôle doivent avoir un espace entre le mot clé de l'instruction et la parenthèse ouvrante, afin de ne pas les confondre avec des appels de fonction.

Il est vivement recommandé de toujours utiliser des parenthèses, même dans les situations où elles sont techniquement optionnelles. Leur présence augmente la lisibilité du code et réduit le risque d'erreur logique lors de l'ajout de nouvelles lignes de code.

Pour l'instruction "switch" :

switch (condition) {
case 1:
    action1;
    break;

case 2:
    action2;
    break;

default:
    defaultaction;
    break;
}

Petite astuce: Si vous désirez optimiser votre code, classez les "case" par fréquence d'apparition; le cas le plus fréquent en haut, et le moins fréquent en bas.

Concernant les expressions conditionnelles se composant d'une variable et d'une constante, la variable doit toujours être à droite (Cela évite de faire une affectation fâcheuse si l'on a oublié un '=').

if (CONSTANTE == $foo) { fooFoo(); }
// est à préférer par rapport à:
if ($foo == CONSTANTE) { fooFoo2(); }

Les "Magic Numbers"

Un Magic number est un chiffre inclus dans une condition, tel que 3, 421, 12 qui apparaît dans une structure de contrôle... On ne sait pas ce qu'ils représentent. Dans l'exemple ci-dessous, que représentent 22, 19 et 16? Si les nombres vous semblent évidents le jour où vous programmez, qu'en est-il pour les autres ou vous même 3 jours plus tard? Que faire si ces valeurs viennent à changer?

if      (22 == $foo) { commencerGuerreNucleaire(); }
else if (19 == $foo) { faireRmEtoile(); }
else if (16 == $foo) { boucleInfinie(); }
else { heureuxCarJeSaisCeQueJeFais(); }

Pour ne pas programmer comme un amateur, il faut utiliser un nom réellement significatif, grâce à la commande define(). Cela est également valable pour 0 et 1.

define("WBUSH_AU_POUVOIR", "22");
define("GNICO_PETE_UN_CABLE", "19");
define("ON_EST_PAS_PAYES", "16"); if (WBUSH_AU_POUVOIR == $foo) { commencerGuerreNucleaire(); }
else if (GNICO_PETE_UN_CABLE == $foo) { faireRmEtoile(); }
else if (ON_EST_PAS_PAYES == $foo) { boucleInfinie(); }
else { heureuxCarJeSaisCeQueJeFais(); }

Appels de Fonction

Les fonctions doivent être appelées sans espace entre le nom de la fonction, la parenthèse ouvrante, et le premier paramètre; avec un espace entre la virgule et chaque paramètre; et aucun espace entre le dernier paramètre, la parenthèse fermante et le point virgule. Voici un exemple :

$var = foo($bar, $baz, $quux);

Comme montré ci-dessus, il doit y avoir un espace de chaque côté du signe égal utilisé pour affecter la valeur de retour de la fonction à une variable. Dans le cas d'un bloc d'instructions similaire, des espaces supplémentaires peuvent être ajoutés pour améliorer la lisibilité :

$courte          = foo($bar);
$longue_variable = foo($baz);

Définitions des Fonctions

La déclaration des fonctions respecte l'indentation classique des parenthèses :

function fooFunction($arg1, $arg2 = '')
{
    if (condition) {
        statement;
    }
    return $val;
}

Les arguments possédant des valeurs par défaut vont à la fin de la liste des arguments. Il faut toujours chercher à retourner une valeur ayant un sens lorsque cela est possible. Voici un exemple un peu plus long :

function connect(&$dsn, $persistent = false)
{
    if (is_array($dsn)) {
        $dsninfo = &$dsn;
    } else {
        $dsninfo = DB::parseDSN($dsn);
    }

    if (!$dsninfo || !$dsninfo['phptype']) {
        return $this->raiseError();
    }

    return true;
}

Commentaires

La documentation des classes incluse dans le code source doit suivre la convention PHPDoc, similaire à celle de Javadoc. Pour plus d'information au sujet de PHPDoc vous reporter à : http://www.phpdoc.de/

Les commentaires non inclus dans la documentation sont vivement encouragés. La règle générale est que, si en regardant une portion de code, vous pensez "Wououh, je ne veux pas me lancer dans cette explication", il faut absolument la commenter tout de suite avant que vous n'oubliez comment cela fonctionne.

Les commentaires du type C (/* */) et les commentaires standard C++ (//) sont tous les deux acceptés. Les commentaires de type Perl/shell (#) sont à éviter.


Inclure du Code

A chaque endroit où vous voulez inclure de façon inconditionnelle un fichier de classe, utilisez require_once(). A chaque endroit où vous voulez inclure de façon conditionnelle un fichier de classe (par exemple des méthodes de construction), utilisez include_once(). Ces deux méthodes s'assurent que le fichier classe n'est inclus qu'une seule fois. Elles partagent la même liste de fichiers, il est donc possible de les mélanger - un fichier inclus avec require_once() ne sera pas inclus une seconde fois par include_once(). Concernant la syntaxe, plusieurs possibilités existent; nous préférons que tout le monde utilise celle-ci:

include_once('exemple.php');
require_once('exemple.php');

Indentation

Utilisez une indentation de 4 espaces, sans tabulation. Si vous utilisez Emacs pour éditer le code, vous devez fixer le paramètre indent-tabs-mode à 'nil'. Vous trouverez ici un exemple de configuration de Emacs pour respecter ces recommandations:

(defun php-mode-hook ()
  (setq tab-width 4
        c-basic-offset 4
        c-hanging-comment-ender-p nil
  	indent-tabs-mode
	(not
	 (and (string-match "\.php$" (buffer-file-name))))))

Commentaires d'En-tête

Tout fichier de code source se trouvant dans le projet doit contenir le bloc de commentaires suivant comme en-tête : (ce qui est en italique est à modifier)

// +----------------------------------------------------------------------+
// | Projet ENGREF         TT n°?          Package ????                   |
// +----------------------------------------------------------------------+
// | Liste des fonctions: (pour un fichier include PHP)                   |
// | - fonction1(...)                                                     |
// | - fonction2(...)                                                     |
// |                                                                      |
// | Page ?????: (nom de la page web)                                     |
// | rapide description en 1 ou 2 ligne                                   |
// | Date: DD-MM-YYYY                                                     |
// +----------------------------------------------------------------------+
// | This file is subject to the terms of the GNU General Public License  |
// | as published by the Free Software Foundation. A copy of this license |
// | is included with this software distribution in the file COPYING.     |
// | If you do not have a copy, you may obtain a copy by writing to the   |
// | Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.    |
// +----------------------------------------------------------------------+
// | Copyright (c) 2003                                                   |
// +----------------------------------------------------------------------+
//

Exemple d'URLs

Utiliser "exemple.com" pour tous les exemples d'URLs (RFC 2606).


Conventions de Nom

Choisissez des noms ni trop longs : "$email_du_destinataire", ni trop courts : "$edd", mais par exemple (deux mots maximum) : "$email_dest".
Attention ! Le Php est "case sensitive" ("email_dest" != "Email_dest")

Classes

Les classes doivent avoir un nom parlant. Eviter les abréviations lorsque cela est possible. Les noms de classes doivent toujours commencer par une majuscule. L'architecture hiérarchique des classes se retrouve aussi dans le nom de la classe, chaque niveau de la hiérarchie séparé par un trait souligné ( _ ). Voir la rubrique Package ci-dessus.

Fonctions et Méthodes

Les fonctions et les méthodes doivent être nommées en utilisant le style "studly caps". Les fonctions doivent avoir le nom du package comme préfixe, pour éviter les doublons entre les packages. La première lettre du nom (après le préfixe pour une fonction) est une minuscule, et chaque premier caractère d'un nouveau "mot" doit être une majuscule. Quelques exemples :

connect()
obtenirDonnees()

Les éléments (méthodes, attributs) privés d'une classe sont précédés d'un simple souligné (_) (ces éléments sont destinés à n'être utilisés que par la classe qui les déclare; PHP ne supportant pas encore le contrôle des noms privés). Par exemple :

_trier()
_initialiserArbre()
$this->_status

Un nom de fonction doit suggérer une action (par un verbe : ex: "verifieFormulaire()" ). Les noms de variables doivent suggérer une propriété ou un nom (ex: hauteur, nom_utilisateur). De plus, les noms doivent toujours être prononçables. Nous fonctionnons à la française, c'est à dire que les fonctions set / get seront nommées fixer / obtenir.

Constantes

Variables

Variables Globales

Les constantes doivent toujours être en majuscules, les mots séparés par des '_'. Préfixez les noms des constantes avec le nom en majuscule de la classe/package dans laquelle elle est utilisée. Les variables doivent toujours être en minuscules, les mots séparés par des '_'. Le recours aux variables globales devra être évité au maximum ou justifié de manière très rigoureuse.
Si votre package a besoin de définir des variables globales, leurs noms doivent commencer par un simple '_' suivi par le nom du package et un autre '_'.
DB_CONSTANTE_DU_PACKAGE_DB
$variable_a_gnico = "toto";
$_DB_variable_globale_du_package

Les arguments dans les déclarations de fonction sont précédés d'un underscore. ex: $_nom_de_la_variable

function foo($_attribut1, $_attribut2, ...)

Les effets de bords (modification de la valeur d'une variable) sur les variables globales est strictement interdit, il faut obligatoirement passer la variable globale en paramètre par référence de la fonction si elle doit être modifiée.

Enfin n'utilisez pas de mots ou abréviations toutes en majuscules:

Utilisez: getStatistiquesHtml()
N'utilisez pas: getStatistiquesHTML()

Les variables temporaires

En règle générale, une variable temporaire est utile si elle contribue à améliorer la lisibilité de votre code (lorsqu'elle remplace une longue expression par exemple). De plus elle doit être utilisée au moins deux fois. Si aucune de ces deux conditions n'est réunie, c'est que vous n'en avez pas besoin !

Autre possibilité: améliorer les performances de votre code en réalisant une seule fois une opération coûteuse au niveau temps d'exécution. Example:

<?php
$max = filesize('myfile.dat');
for ($i = 0; $i < $max; $i++) { /*votre code*/ }
?>
// est à préférer par rapport a
<?php
for ($i = 0; $i < filesize("myfile.dat"); $i++) { /*votre code*/ }
?>

N'utilisez pas de '-' (tiret) pour séparer des mots

En effet, cela génère des warning lorsqu'on l'utilise avec des " " (magic quotes) :

$mon_tableau['foo-bar'] = 'Bonjour';
print "$mon_tableau[foo-bar] tout le monde"; // warning message $mon_tableau['foo_bar'] = 'Bonjour';
print "$mon_tableau[foo_bar] tout le monde"; // will output: Bonjour tout le monde

Conseils divers

Print et Printf

Sur la documentation on peut lire pour les fonctions "printf" et "print" respectivement "affiche une chaîne formatée" et "affiche une chaîne".

Cela signifie que "printf" ne doit être utilisée que lorsque la chaîne de caractères que l'on cherche à afficher contient des valeurs que l'on souhaite "formater", par exemple :

Un euro, arrondi à 2 chiffres apres la virgule vaut :<br>
  <?
  $euro = 6.55957;
printf ("%.2f\n FF<br>", $euro);
?>

Par contre pour afficher...

Les membres de l'équipe sont :
<?
$dev1 = "Anne-Sophie R.";
$dev2 = "José F.";
$dev3 = "Pierre GB";
print("$dev1 $dev2 $dev3");
?>

... il faut utiliser "print" car il n'y a pas de chaînes à formater.

Rappelez-vous qu'avant d'afficher la chaîne de caractères, la fonction "printf " la formate systématiquement, ralentissant au passage l'exécution.

Différence entre ' et " (simple et double guillemets)

Les chaînes contenues entre ' ne sont pas interprétées, mais affichées telles quelles. Si elle contient une variable, celle-ci ne sera pas remplacée. Lorsque l'on utilise les ", la chaîne est interprétée, c'est à dire que les variables seront remplacées par leur valeur lors de l'affichage. Les " sont donc plus lentes à l'exécution. Voir les exemples:

$world = World;
print ('Hello $world');              // donnera                 Hello $world
print ("Hello $world");              // donnera                 Hello World
print ('Hello '.$world);             // donnera                 Hello World

Ne pas réinventer la roue

Une erreur classique lorsqu'on découvre un langage : avoir tendance à tout réécrire soi-même au lieu d'utiliser les fonctions prévues à cet effet. Deux conséquences à cela :
- L'exécution du programme est ralentie.
- Votre code perd en lisibilité.

Vous souhaitez inverser une chaîne de caractères ? Calculer sa longueur ? Avant de foncer tête baissée dans des boucles "for" et autres variables temporaires, parcourez la documentation ! Manuel de référence PHP en version française ou Php.net

Si jamais vous pensez qu'une fonction que vous êtes en train de développer pourrait servir à d'autres personnes, n'hésitez pas à en faire une librairie, même petite!

Tests sur retour de fonction

Ne pas vérifier l'égalité du retour d'une fonction avec 1 (ou true). Il est préférable de tester l'inégalité par rapport à 0 (ou false). La plupart des fonctions retournent 0 si c'est faux, mais non-zéro si c'est vrai. Exemple:

if (false != foo()) {...}
// est préférable à:
if (true == foo())  {...}
// la meilleure chose a faire reste:
if ( foo() ) {...}

Tests étranges

Les tests contenant des égalités sont toujours perturbateurs: le programmeur a-t-il vraiment voulu faire cela? Parfois oui, mais souvent non. La solution est "Just not do it", l'inverse de la philosophie de Nike. A la place, utiliser des tests explicites, ne jamais faire de test implicite.

if ($abool = $bbool) { ... }             // c'est confusant...
//mieux vaut:
$abool = $bbool;
if ($abool) { ... }

Sécurité

Attention : si le PHP est capable de stocker certaines données comprenant des caractères spéciaux, il n'en est pas forcément de même pour la base de données. L'utilisation de la fonction "addslahes" peut alors aider:

string  addslashes ( string  str )
addslashes retourne la chaîne str , après avoir échappé tous les caractères qui doivent l'être, pour être utilisé dans une requête de base de données. Ces caractères sont les guillemets simples ( ' ), guillemets doubles ( " ), anti-slash ( \ ) et NUL (le caractère NULL ). Voir aussi stripslashes , htmlspecialchars et quotemeta .

Concernant les formulaires, ne pas utiliser de champs cachés ("hidden"), ne pas passer les mots de passe en clair dans l'URL!
Le Javascript étant formellement interdit (cf. Normalisation), si vous avez besoin de tests de champs, ceux-ci doivent être exécutés du côté serveur.

Une autre chose utile est de cacher les warning grâce à la commande @ avant chacune des expressions qui pourrait provoquer un message d'erreur. Un exemple: Si "fichier.php" ne peut être ouvert, les utilisateurs liront:
WARNING: fopen('fichier.php, 'r') - No such file or directory in http://\www\yourdirectory\votrefichier.php on line 28
En écrivant @fopen('fichier.php'); les messages d'erreurs disparaissent.
! ATTENTION !
Dans la phase de développement, ces warnings peuvent être très intéressants pour débugger votre code. Attention à ne mettre les @ dans votre code trop tôt, surtout pas durant une phase de test. D'autre part, les codes d'erreurs peuvent être récupérés pour ensuite être traités et affichés (certaines erreurs doivent être prévues et anticipées). Utilisez donc les @ avec discernement et seulement lorsque votre code est au point.

Faire relire le code

N'hésitez pas à faire relire votre code par une personne qui aura un oeil neuf sur le code que vous venez de produire. Des bugs peuvent ainsi être décelés bien plus rapidement que si c'est vous qui vous étiez relu. C'est aussi l'occasion de revoir sa documentation si celle-ci est jugée peu lisible.

Ecrire des fonctions courtes

Les méthodes doivent se limiter au maximum à une page de code (cf Algorithmique).

Optimisation

Les possibilités d'optimisation de code PHP sont énormes. Pourquoi n'avons nous pas plus insisté sur ce point? Tout simplement car nous pensons qu'un programme fonctionnel est plus important que le gain de quelques millisecondes par instruction. Cependant, si vous désirez en savoir plus, voire optimiser votre code, n'hésitez pas à venir nous en parler. Nous disposons d'un grand nombre de publications et conseils sur ce sujet.

Pas de "Copier (Alt-W) / Coller (Ctrl-Y sous Emacs)" sans comprendre!


Sources

Ce document est issu du document original des consignes de codage de PEAR (http://pear.php.net/), d'un tutorial issu du site JDNetDeveloppeurs (http://developpeur.journaldunet.com/tutoriel/php/011113php_10erreurs.shtml) et d'une suite de documents dont voici la liste:

http://utvikler.start.no/code/php_coding_standard.html
http://www.4webhelp.net/tutorials/php/coding_tips.php
http://clue.denver.co.us/development/index6.html
http://www.phpbuilder.com/columns/weerning20021209.php3?page=3
Si jamais vous pensez que nous vous avons copié un document dont vous êtes propritéaire et que vous ne figurez pas dans la liste, merci de nous le faire savoir pour que nous puissions rectifier cette erreur au plus vite.
Qu'est-ce que PEAR ? PEAR est l'abréviation de "PHP Extension and Application Repository". La mission de PEAR est de fournir :

Voilà pourquoi leur document est une excellente base pour notre guide de développement. Bonne lecture!!!

Valid XHTML 1.0!