Difference between revisions of "Language and development rules"

From Dolibarr ERP CRM Wiki
Jump to navigation Jump to search
m (→‎Les dates et TimeZone: partial translation)
Line 68: Line 68:
  
 
* Functions must return a value strictly higher than 0 if successfull and strictly lower than 0 if error.
 
* Functions must return a value strictly higher than 0 if successfull and strictly lower than 0 if error.
 +
 +
* No dead code into Dolibarr core code (code used by external modules only must be included into external modules).
  
 
= SQL rules =
 
= SQL rules =

Revision as of 20:51, 28 May 2012

This is some rules on language, syntax and norm we use into Dolibarr project:

Versions

  • Dolibarr must works on:
  1. All OS (Windows, Linux, MACOS...)
  2. PHP 7.1.0+ (Must works with no need of complementary PHP module, except module to PHP module to access database).
  3. Mysql 5.1+

Copyright Norms

  • All PHP files must start with a header that looks like
<?php
/* Copyright (C) YYYY John Doe  <email@email.com>
 *
 * Licence information
 */
...

When you edit an existing file of project, you must add a Copyright line under others.

PHP Norms

  • Dolibarr is written with PHP and supports all PHP version higher than 7.1.0+. All files must end with extension .php
  • Usage of superglobals PHP variables must use dedicated operators $_COOKIES, $_SERVER, $_ENV but use dolibarr function GETPOST() to get content of $_GET or $_POST..

Other operators ($HTTP_SERVER_GET, ...) are now deprecated inside PHP, so they must no more be used. Like that code will works alos if option register_long_arrays is to off. Moreover, the code must works when PHP option register_globals is off (recommanded by PHP). It must works the same way that when option register_globals is on (by default on a lot of installations).

  • Smart tags PHP are not used. PHP code section must start with <?php
  • Do not use PHP_SELF. Use instead $_SERVER["PHP_SELF"].
  • When several variables must be initialized with same value, you must use several lines
$var1=1;$var2=1;$var3=1;

instead of

$var1=$var2=$var3=1;

that is slower.

  • String must be rounded by simple quote and vairable inside string must be out of the quote.
print 'My text show my '.$variable.' !';
  • Comments must use the C syntax, ie a double antislash for a comment on one line and a slash-star to open a bloc for several lines
/* Bloc of comment
 *
 * End of bloc
 */

$monobjet = new MonObjet($db);
$result=$monobjet->fetch($idobject);

for ($i = 1 , $i < 2 ; $i++)
{
  // comment on one line
  print $i;
}
  • Files must be saved with Unix format (LF) and not Windows (CR/LF). Unif format is compatible on all OS like Unix like, Windows, Mac, but the Windows text file format is no working on some PHP under Unix.
  • Functions must return a value strictly higher than 0 if successfull and strictly lower than 0 if error.
  • No dead code into Dolibarr core code (code used by external modules only must be included into external modules).

SQL rules

  • All SELECT * are forbidden ! Any SELECT must define complete list of fields to get. This avoid confusion. Example:
SELECT field_a, field_b, field_c FROM table_1 WHERE field_d = '$id'
  • Into SQL requests, you must quote fields except fields that contains amounts that must be stored as double or real type. Quotes on numbers may results in saving a different value. For example 412.62 in an insert will be saved with value 412.61999512 into database fi target field has type double(24,8). Only PHP see value 412.61999512. Other tools will see 412.62 giving sensation that there is no problem. But it's PHP that hase the good vision. There is really a wrong value into database. By removing quotes on numbers, no problem occurs.

Example:

Good:     INSERT INTO table_1 (field_txt, field_num) VALUES ('txt', 412.62)
Bad:      INSERT INTO table_1 (field_txt, field_num) VALUES ('txt', '412.62')

Note, problem of float numbers is same problem on all langauges and not only when inserting data into database. It occurs also with any language when you work on "real" numbers, so numbers must be, as soon as they are affected, cleaned with function price2num with second parameter defined to : 'MU', 'MT' or 'MS' depending on usage of number. (see function documentation)

Well, to be compatible with any accurency required by any country on amounts, we will use the following type into database:

- double(24,8) for any amount
- double(6,3) for any vat rate
- real for a quantity
  • Dolibarr must works even if Mysql option strict is active.

To activate it (recommanded when developping on Dolibarr), add the following line into the config file of your Mysql server (my.cnf)

sql-mode="STRICT_ALL_TABLES,ONLY_FULL_GROUP_BY,NO_ZERO_DATE"
  • Functions NOW or SYSDATE are forbidden inside SQL requests. If you must use the current date into a field, value must come from the PHP and not from the database engine. This is to avoid a better portability of code and a correct management of TimeZone.

HTML norms

  • All attributes of HTML tags must be in lower case and *quoted* with *"*.
  • Links href must be absolute and use the function dol_buildpath() to get absolute path from a relative path and img tag must be build using function img_picto().

For example:

print '<a href="'.dol_buildpath('/mydir/mypage.php').'">'.img_picto('Texte alt','namepictopng','').'</a>';
  • javascript code and call to javascripts file into php pages must be avoid. Howevr, if you need to include javascript code, you must add a condition on "$conf->use_javascript"
if ($conf->use_javascript)
{
...  // generated php code with javascript here
}
  • Popups windows must not be used, except for tooltips (and must have a condition as explained before).
  • External scripts must be written into Perl if they can't be written into PHP. Usage of another language is not forbidden but must be argue before onto development mailing-list.

Dolibarr norms and code skeleton

En verysmall.png Page waiting for translation. To translate, create an account, go back and clic on "Modify".
Fr verysmall.png Page en attente de traduction. Pour traduire, créez un compte, revenez et cliquez sur "Modifier".
Es verysmall.png Página a traducir. Para traducirla, cree una cuenta, vuelva a la página y haga clic en "editar".
It verysmall.png Pagina da tradurre. Per tradurla, crea un utente, torna indietro e clicca su "modifica".
Pt verysmall.png Página a aguardar por tradução. Para traduzir, crie uma conta, volte atrás e clique em "Modificar".
De verysmall.png Seite wartet auf Übersetzung. Um Übersetzung zu erstellen, richte einen Account ein, geh zurück und klicke auf "bearbeiten".
Zh verysmall.png 页面等待翻译。若要翻译本页,先创建一个帐户、登录并返回本页后单击“编辑”。

Skeleton code

To standardize the code, and to speed up the development of new components in Dolibarr, you'll find 4 skeletons fully prepared in the directory dev/skeletons.

  • 1 that serves as an example of the module description: myModule.class.php
  • 1 that serves as an example of code for creating a new class: skeleton_class.class.php
  • 1 that serves as an example of code for creating a new page: skeleton_page.php
  • 1 that serves as an example of code for creating a script for executing command lines: skeleton_script.php

Use it as an example. Note that the skeletons are also used by the PHP code generator, which is described in the development chapter of Dolibarr modules, to speed up your development.

Dates and Timezones

Dolibarr wants an application that is multi-user and multi-location. It's therefore necessary to store dates in the right format. To avoid problems with conversions the following rules should be applied:

  • 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, 3 heures à Paris (TZ=+1) = 2 heures à Greenwitch (TZ=0) sera stocké en mémoire 7200 et sera soumis à la base de données avec la chaine '19700101030000' (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 ('19700101030000') 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

Add logs to your code using function

dol_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';
dol_mkdir($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 ways to organize code:

  • The Transaction Script (The source code is linear for each user action).

This is the old school used by all procedural languages. Inconvenient: Redundancy of code. Need to know the physical model of data to develop.

  • The Domain Model

This notion is available with object languages. It is business process (to identify before) that are used for objects classes. Inconvenient: Model very complex to maintain.

  • The Table Module

This is a mix between 2 previous where we have only one unique class for each table of database.

-> As shown in code skeletons (see previous chapter), Dolibarr use concept of Table Module.

Communication between business logic - data (ORM)

En verysmall.png Page waiting for translation. To translate, create an account, go back and clic on "Modify".
Fr verysmall.png Page en attente de traduction. Pour traduire, créez un compte, revenez et cliquez sur "Modifier".
Es verysmall.png Página a traducir. Para traducirla, cree una cuenta, vuelva a la página y haga clic en "editar".
It verysmall.png Pagina da tradurre. Per tradurla, crea un utente, torna indietro e clicca su "modifica".
Pt verysmall.png Página a aguardar por tradução. Para traduzir, crie uma conta, volte atrás e clique em "Modificar".
De verysmall.png Seite wartet auf Übersetzung. Um Übersetzung zu erstellen, richte einen Account ein, geh zurück und klicke auf "bearbeiten".
Zh verysmall.png 页面等待翻译。若要翻译本页,先创建一个帐户、登录并返回本页后单击“编辑”。

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).