Language and development rules
This is some rules on language, syntax and norm we use into Dolibarr project:
Versions
- Dolibarr must works on:
- All OS (Windows, Linux, MACOS...)
- PHP 4.3 or + (Must works with no need of complementary PHP module, except module to PHP module to access database).
- Mysql 3.1 or +
PHP Norms
- Dolibarr est écrit en PHP et supporte toutes versions PHP supérieures à la 4.1. Les fichiers doivent tous comporter l'extension .php
- L'appel aux variables superglobales PHP doit passer par les opérateurs dédiés $_GET, $_POST, $_COOKIES, $_SERVER, $_ENV.
Les autres opérateurs ($HTTP_SERVER_GET, ...) ayant été passés en deprecated au sein de PHP, ne doivent plus être utilisés. Ainsi le code doit fonctionner y compris quand l'option register_long_arrays est à off. De plus, le code doit fonctionner quand l'option PHP register_globals est à off (recommandé par PHP) aussi bien que quand l'option register_globals à on (par défaut sur de nombreuses installations).
- Les smart tags PHP ne sont pas utilisés. Les sections de code doivent commencer par <?php
- Pas d'utilisation de la variable PHP_SELF. Utiliser a la place $_SERVER["PHP_SELF"]
- Quand plusieurs variables doivent être initialisées avec la même valeur, il faut utiliser plusieurs lignes
$var1=1;$var2=1;$var3=1;
plutôt que
$var1=$var2=$var3=1;
qui est moins performant.
- Les chaines doivent être encadrés de simple quote et les variables sorties de la chaine.
print 'Mon texte affiche ma '.$variable.' !';
- Les commentaires doivent suivre la syntaxe C, ie un double antislash pour un commentaire d'une ligne et utilisation slash-étoile pour ouvrir un bloc de plusieurs lignes
/* Bloc de commentaire
*
* Fin du bloc
*/
$monobjet = new MonObjet($db);
$result=$monobjet->fetch($idobject);
for ($i = 1 , $i < 2 ; $i++)
{
// commentaire sur une ligne
print $i;
}
- Les fichiers doivent etre sauvés en format Unix (LF) et non Windows (CR/LF). Le format Unix étant compatible sur les OS Unix like, Windows, Mac, alors que le format fichier texte Windows pose problème sous certains PHP sous Unix.
Les fonctions doivent retourner 0 en cas de succès, et un nombre <0 en cas d'erreur. A ce jour, très peu de fonctions respectent ce standard mais c'est celui vers lequel il faut tendre.
Normes SQL
- Les SELECT * sont interdits ! Chaque SELECT doit spécifier la liste complète des champs à récupérer. Cela permet d'éviter les confusions. Exemple:
SELECT field_a, field_b, field_c FROM table_1 WHERE field_d = '$id'
- Dans les requêtes SQL, on quote les champs mais pas les numériques qui contiennent des montants destinés à être stockés dans des champs de type double ou real. Les quotes sur les numériques de type float provoquent parfois un stockage d'une valeur différente. Par exemple 412.62 dans le insert sera en fait stocké avec la valeur 412.61999512 en base si le champ est de type double(24,8). Et seul le PHP voit 412.61999512. Les autres outils verront 412.62 donnant l'impression qu'il n'y a pas de problème. Et c'est le PHP qui a raison, il y a problème en base. En supprimant les quotes sur les numériques, cela va mieux.
Exemple:
Bon: INSERT INTO table_1 (field_txt, field_num) VALUES ('txt', 412.62)
Mauvais: INSERT INTO table_1 (field_txt, field_num) VALUES ('txt', '412.62')
Remarque, le pb des float est général et pas seulement sur les accès base, il est présent dans tous les languages quand on travaille sur des nombres réels, aussi ils doivent être, dès que affectés, nettoyés par la fonction price2num avec le 2eme paramètre renseigné à: 'MU', 'MT' ou 'MS' selon le besoin. (voir doc fonction)
Dans la pratique, afin d'etre compatible avec toutes les précisions des pays, on utilisera les types suivants:
- double(24,8) pour tout montant - double(6,3) pour les taux de tva - real pour une quantité
- Dolibarr doit fonctionner même avec l'option strict de Mysql active.
- Les fonctions NOW ou SYSDATE sont interdites au sein des ordres SQL. S'il faut saisir la date du moment dans un champ, la valeur doit venir du PHP et non du moteur de base de données. Ceci afin d'avoir une meilleure portabilité du code mais surtout d'avoir une gestion correcte des TimeZone.
Normes HTML
- Tous les attributs dans les balises HTML doivent être *en minuscule* et quotés avec des *doubles quote* (Norme xhtml)
- Les liens href doivent être absolus et se baser pour les pages sur la constante DOL_URL_ROOT qui pointe sur htdocs et pour les images se base sur l'appel de la fonction img_picto.
Par exemple:
print '<a href="'.DOL_URL_ROOT.'/monrep/mapage.php">'.img_picto('Texte alt','nompictopng','').'</a>';
- Le javascript et l'appel aux scripts java dans les pages php est à proscrire. Si toutefois du code javascript est inclus, il doit être conditionné par le test sur "$conf->use_javascript"
if ($conf->use_javascript)
{
... // Le code php qui génère du javascript est ici
}
- Les popups windows ne doivent pas être utilisées, sauf pour des tooltips (et restent condtionnées par le point ci-dessus).
- Les scripts externes sont écrits en Perl s'ils ne peuvent l'être en php, l'utilisation d'un autre langage n'est pas interdit mais doit être discuté au préalable dans la mailing list des développeurs. Le langage devra être maitrisé par au moins 2 développeurs pour en assurer la maintenance.
Normes Dolibarr et squelettes de code
Squelettes de code
Afin d'uniformiser le code et d'accélérer le développement de nouveaux composants dans Dolibarr, se trouvent dans le répertoire dev/skeletons, 4 squelettes de code tout préparés.
- 1 qui sert d'exemple de descripteur de module: myModule.class.php
- 1 qui sert d'exemple de code pour faire une nouvelle classe: skeleton_class.class.php
- 1 qui sert d'exemple de code pour faire une nouvelle page: skeleton_page.php
- 1 qui sert d'exemple de code pour faire un script à exécuter en ligne de commande: skeleton_script.php
Servez-vous en comme exemple. Notons que ces squelettes sont aussi utilisés par le générateur de code PHP qui est décrit dans la chapitre de développement de module Dolibarr pour accélérer vos développement.
Les dates et TimeZone
Dolibarr se veut une application mutli-utilisateur et multi-localisation. Il convient donc de stocker les dates dans le bon format. Pour éviter les problèmes de conversion, les règles suivantes doivent être appliquées:
- Une date en mémoire doit être au format Timestamp GMT.
- Une date stockée en base de données contient le Timestamp GMT en rapport avec la date soumise dans la requête en heure locale du serveur PHP. Cela ne concerne pas les dates mises à jour automatiquement par la base (champ tms en base).
Ainsi le 1er janvier 1970, 2 heures à Paris (TZ=+1) sera stocké en mémoire sera soumis à la base de données avec la chaine '19700101020000' (PHP convertit en heure de son TZ et la base déconvertit avec son TZ qui est le même que celui de PHP).
Les méthodes select doivent donc traduire les champs dates lus qui sont au format chaine TZ de la base ('19700101020000') par appel de la méthode db->jdate afin de récupérer une information en mémoire au format Timestamp GMT. Et les méthodes insert doivent lors de la génération de la requête convertir la date mémoire connue en variable, par la méthode db->idate (Voir exemples générés par le squelette).
- Les dates mises à jour automatiquement par la base (champ tms en base) contiennent le Timestamp GMT au moment où la modification est faite. Les méthodes select récupèrent directement cette donnée en mémoire au format Timestamp GMT.
L'encodage UTF8/ISO
Dolibarr stocke les informations de manière suivante:
- En base de donnée, les données sont en UTF8 ou ISO. Cela dépend du pagecode de la base, donc des options à la création de cette base. Dans tous les cas le driver d'accès base Dolibarr (dans /lib/database) s'arrange à la lecture et insertion pour convertir vers et depuis de l'UTF8.
- Les données en mémoires sont donc stockées en UTF8 (instances d'objets PHP).
- Les pages web affichées à l'écran sont au format UTF8 (avec les versions < 2.5.1, c'est le vieux paramètre $character_set du fichier conf.php qui définissait le format de sortie).
Les nombres réels, montants et calculs
En PHP comme dans d'autres langages (Java par exemple), les données non entières (float, real, double) ne sont pas fiables. Essayer de faire par exemple
print 239.2 - 229.3 - 9.9;
Vous n'obtiendrez pas zéro mais un nombre très petit en puissance de 10 négative. Si vous obtenez zéro, vous pourrez trouvez d'autres exemples qui ne fonctionnent pas. Le problème des float est général, une variable résultante de calcul de nombre réels doit SYSTEMATIQUEMENT être nettoyée par la fonction price2num avec le 2eme paramètre renseigné à: 'MU', 'MT' ou 'MS' selon le besoin (voir doc fonction).
print price2num(239.2 - 229.3 - 9.9, 'MT');
S'il ne s'agit pas d'un prix sur lequel s'adapte les paramètres MU, MT ou MS, il faut utiliser la fonction round().
Création de tables
Ne pas créer de table à l'utilisation, c'est-à-dire à la première utilisation du module. Si vous créez un module qui utilise des tables qui ne sont pas intégrées en standard dans le code de Dolibarr, veillez à suivre le didacticiel Créer un module qui explique comment joindre des tables qui se créent à l'activation du module et non à son utilisation (voir plus haut).
Logs
Ajouter des traces dans votre code avec la fonction dolibarr_syslog($yourmessage, LOG_INFO|LOG_DEBUG|LOG_WARNING|LOG_ERR);
Répertoires de travail
Si vous avez besoin de créer un répertoire de travail, dans votre code, faites référence à ce répertoire par DOL_DATA_ROOT.'/monmodule'. Le répertoire peut être créé dans votre code à l'exécution par le code suivant:
$mymoduledir=DOL_DATA_ROOT.'/monmodule';
if (! is_dir($mymoduledir)) create_exdir($mymoduledir);
Si vous avez besoin d'un répertoire qui contiendra des données temporaires, ce répertoire doit être DOL_DATA_ROOT.'/monmodule/temp'
Design patterns and Object programming
Creation design patterns (GoF)
Design patterns defined by the Gang Of Four (see wikipédia on Design patters). No usage of such patterns is required. We found some objects next to Singletons or Factory but not completely compliant with syntax, this is to be compatible with PHP 4 that is not a pure object language.
Structure design patterns (GoF)
Design patterns defined by the Gang Of Four (see wikipédia on Design patters). No usage of such patterns is required.
Behavior design patterns (GoF)
Design patterns defined by the Gang Of Four (see wikipédia on Design patters). No usage of such patterns is required.
Design patterns of enterprise (Martin Fowler)
Patterns of code organization
Martin Fowler has identified 3 way to organize:
- The Transaction Script (The source code is linear for each user action).
This is the old schoold used by all procedural languages. Inconvenient: Redundancy of code. Need to know the physical model of data to develop.
- The Domain Model
C'est un concept possible depuis les langages objets. Ce sont les procédures métiers (qui doivent être identifiés avant) qui servent de bases pour les classes objets. Inconvénient: Motif complexe à maintenir.
- The Table Module
This is a mix between 2 previous where we have only one unique class for each table of database.
Comme le montre les squelettes de code (voir point précédent), Dolibarr se base sur le principe du Table Module.
Communication Logique métier - Données (ORM)
Il existe 3 modes de liaisons:
- Le Table And Row Data Gateway
C'est le plus simple. On crée une table par classe et chaque classe est un pont avec la table correspondante, voir une classe par ligne de table. Une instance de classe étant alors un enregistrement de la table. La classe ne contient que du code d'accès aux lignes ou colonnes de tables.
Exemple: C'est le mode mis en oeuvre quand on utilise certains Frameworks d'ORM comme iBatis (http://ibatis.apache.org/).
- Le Active Record
Identique au précédent, mais on se permet d'ajouter quelques fonctions métiers sur la classe, à conditions que ces fonctions soient propres à la table ou à l'enregistrement.
Exemple: C'est le mode choisi pour les développements Dolibarr et de nombreuses autres applications PHP qui ont leur propre framework et pratiques de développements.
- Le Data Mapper
Les classes représentent les entités du problème et non les données. Il faut donc doubler, tripler... ces classes avec des classes Mapper pour accéder aux données. Plus "puriste" sur le papier car plus proche du métier, ce mode a aussi l'inconvénient d'être plus complexe sur le plan pratique. Exemple: C'est le choix si on utilise le Framework d'ORM Propel (http://propel.phpdb.org/trac/). On le trouve donc sur des applications plus lourdes basées sur cet ORM entre autres.
Pour les développements Dolibarr, il est recommandé d'utiliser le mode de liaison Active Record qui offre les avantages d'un modèle proche du métier sans en avoir la complexité et sans trop masquer non plus la technique. C'est dans ce mode que le développement, la compréhension du code et la maintenance technique et/ou fonctionnelle semble la plus productive (ceci est toutefois un éternel débat entre les puristes et les pragmatiques, débat dans lequel personne ne peut vraiment avoir raison, car tout dépend des objectifs à atteindre).