Language and development rules

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  (Must works with no need of complementary PHP module, except module to PHP module to access database).
 * 3) Mysql

= Copyright Norms = When you edit an existing file of project, you must add a Copyright line under others.
 * All PHP files must start with a header that looks like

= PHP Norms =
 * Dolibarr is written with PHP and supports all PHP version higher than . All files must end with extension .php

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 (recommended by PHP). It must works the same way that when option register_globals is on (by default on a lot of installations).
 * Usage of superglobals PHP variables must use dedicated operators $_COOKIES, $_SERVER, $_ENV but use Dolibarr function GETPOST to get content of $_GET or $_POST..


 * Do not use PHP_SELF. Use instead $_SERVER["PHP_SELF"].

instead of that is slower.
 * When several variables must be initialized with same value, you must use several lines


 * String must be rounded by simple quote and variable inside string must be out of the quote.


 * Comments must use the C syntax, ie a double anti-slash for a comment on one line and a slash-star to open a bloc for several lines


 * Functions must return a value strictly higher than 0 if successful and strictly lower than 0 if error.


 * No dead code (code never used) into Dolibarr core code (code used by external modules only must be included into external modules).


 * Use "include_once" for anything with functions or class definitions in it (so *.class.php and *.lib.php files), use "include" for template-style php with files containing a mix of HTML and PHP (so *.inc.php and *.tpl.php files).

You can use the file dev/codesniffer/ruleset.xml as rule file to control coding style with PHPCodeSniffer.
 * Coding style to use is the PSR-2 (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) with 2 exceptions, this means:
 * 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.
 * Smart tags PHP are not used. PHP code section must start with <?php
 * But there is an exception of length of line: We accept more than 80 characters per line. 80 is really to low. In most cases, we never go over 80, but sometimes (when declaring a list of reference arrays, like into module descriptor files), it is better to have long lines instead of long page with code content that is just data declaration and does not contains any logic.
 * The other exception is that we don't replace the tab with space. This make lot of editor crazy and break some auto-format features broken.

= SQL rules =

Table structures
When you create a new table, it is recommended to use the same conventions as other Dolibarr tables. This means the following fields: - rowid integer for id - tms timestamp that will contain date of last modification (the database manages this field automatically, no need to manage it by the code, just create the field) - import_key that will contains the import code YYYYMMDDHHMMSS if you make mass import. Eventualy - datec timestamp that is the creation date - fk_user_creat integer that is the id of user making creation - fk_user_modif integer that is the id of user making change
 * Structure of tables.

Note field: - note_private text for private comment of the object - note_pubic text for public comment of the object or - note text if there is no need to have private and public

Well, to be compatible with any accuracy required by any country on amounts, and with any database syntax, we will use the following type into database: - integer for an any of foreign key - double(24,8) for any amount - double(6,3) for any vat rate - real for a quantity - varchar for a string (also if length is 1, the type char is more and more deprecated) - timestamp for a field date+time that is automatically updated - datetime for a field date+time - date for a field date
 * Type of fields:


 * All tables has a prefix to avoid name conflicts with other projects. In current version this prefix is fixed ans can't be modified. Its value is llx_. In a future version this value should be modified during installation process.

Primary keys
The primary key of a table must be called rowid.

Some old tables does not use this rule and use a primary key called id (ie Table llx_c_actioncomm), but the reason is history and this should not happens anymore.

Foreign keys
A foreign key name must start with prefix fk_ followed by the table name linked (this is required to avoid duplicate names in project that are not allowed even if on different tables by some DBMS like Postgresql) then the name of child field (this is to allow to have several foreign keys on same table).

Example: fk_facture_fourn_fk_soc is a foreign key on table llx_facture_fourn for field fk_soc in this table (that link to the rowid field in another table)

Note: If you develop your own external module, it must have no foreign keys that point to Dolibarr standard tables. This will break standard dolibarr upgrades, repair, backup and restore tools and may also break standard features.

Alternative keys
Sometimes, we need another unique keys than primary key. We can add in this case an alternate unique key. When we need this, we can create an alternate unique key. Such an index is called by a name that start by prefix uk_ followed by an underscore, then the the table name (this is required to avoid duplicate names of unique keys that may create problems for some DBMS like Postgresql) and then another string to define the key (this is to allow to have several unique keys on same table).

Example: uk_societe_code_client is a unique key on table llx_societe on field code_client.

Index performance
Some fields are often used as search or order criteria, or for joins. In such case, we need to set a performance index on field to increase performances. Such indexes are named with a prefix idx_ then the table name and then the field name.

Example: idx_societe_user_creat is a performance index on table llx_societe for field user_creat

DDL file format
Files that contain definition of the database structure (DDL files) must be 2 per tables: A comment will be added on each field to explain its usage.
 * Each table is defined by its own file whose name is llx_mytable.sql
 * All foreign keys, performance indexes or other constraints will be defined into another file called llx_mytable.key.sql

Those files must be stored into directory install/mysql/tables for all standard files or mymodule/tables for tables provided by an external module.

Example file of table llx_mytable.sql:

Example file for keys/indexs on llx_mytable.key.sql:

SQL Coding rules
When doing select, we can use alias to simplify writing/reading of requests: However, we must not used alias for update request as they are not compatible with Mysql 3.1.
 * Alias usage/Fields naming


 * All SELECT * are forbidden ! Any SELECT must define complete list of fields to get. This avoid confusion. Example:


 * 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 (due to implicit conversion string to numeric) if 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:

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' (for unit prices), 'MT' (for total prices) or 'MS' (otherwise) depending on usage of number. (see function documentation)


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

Mysql specificities
This format support foreign keys and their restrictions, and transactions integrity is also supported. This guarantee that Dolibarr events keep all data with correct values between tables even even transaction modify different tables.
 * Tables must be declared with format InnoDB.

To activate it (recommanded when developping on Dolibarr), add the following line into the config file of your Mysql server (my.cnf or my.ini)
 * Dolibarr must works even if Mysql option strict is active.

PostgreSQL specificities
Only Mysql SQL files must be maintained. Those files are converted "on the fly" by the database Dolibarr driver.

There is an exception The SQL "UPDATE FROM" : Syntax MySQL :

Syntax PgSQL:

There is no native SQL request "update from" in dolibarr core. But in your module you should do :

= HTML norms =
 * HTML used must be HTML compliant and not XHTML. All attributes of HTML tags must be in lower case and quoted with ".

For example:
 * 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.

Otherwise, we must avoid forcing the column width. Reason is that, in most cases, the browser make a better works to define column width automatically than forced values, and it works whatever is the resolution.
 * HTML tables must have columns with no forced width, except for columns that contains data we know the length. For example, a column with a picto only can be forced to with="20px".


 * Javascript/ajax code and call to javascript files into php pages must be avoided. However, if you need to include javascript code, you must add a condition on "$conf->use_javascript_ajax"


 * 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 =

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 is 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:

For exemple le 1st january 1970, 3 hour at Paris (TZ=+1) = 2 hour at Greenwitch (TZ=0) will be stored into memory with value 7200 and will be submitted into a SQL request to database with the string '19700101030000' (PHP convert into its TZ hour and databse will unconvert it using its timezone too that is same than PHP).
 * A date into memory must always be stored with Timestamp GMT format.
 * A date stored into database, is the GMT Timestamp of date submitted into the request usng the PHP server timezone. This does not apply to date update automatically by database (fields tms into database).

All select methods should translate date fields, that are with format TZ of database ('19700101030000'), into a timestamp field by calling the method db->jdate. This is to store into memory a GTM Timestamp date. All insert methods must convert, during generation of SQL request, the memory date into the string by using db->idate (you may find examples into skeleton).


 * Dates that are updated automatically (field tms into database) contains a GMT Timestamp GMT of date when change is done. The select will also use the db->jdate (that use PHP server TZ) to convert read data into a GMT Timestamp into memory. So if timezone of database differs from timezone of PHP server (one of them is not correctly set), you may experience differences between creation date and update date.


 * Manipulation date with PHP must be done using the Dolibarr date functions: dol_now, dol_mktime, dol_stringtotime, dol_getdate, dol_time_plus_duree. You may also find other functions available into file date.lib.php.

UTF8/ISO encoding
Dolibarr stores data in the following way:
 * In database, data is stored in UTF8 or ISO. It depends on the database's pagecode therefore these options are set at creation time. In any case, Dolibarr's database driver (in lib/database) deals with it to convert from/to UTF8 at insertion and readout.
 * Memory data is stored in UTF8 (PHP object instances).
 * Screen displayed web pages are UTF8 (for versions prior to 2.5.1, the deprecated $character_set parameter from conf.php file defines output format).

Float numbers, amount and calculation
With PHP, like other languages (Java for exemple), non integer data (float, real, double) are not reliable for calculation. Try to make for example You wont get zero but a very small decimal number. If you get zero her, you should be able to find other examples that don't work. Problem of float is general, so a variable that is a result of a calculation using decimal numbers must ALWAYS be cleaned using the function price2num with the econd parameter to: 'MU', 'MT' or 'MS' depending on need (see description of function). If data manipulated is not an amount, so using MU, MT, MS has no sense, so you must use the function round.

Creation of tables
Do not create tables, on the fly, during execution by a standard user, we mean, during current usage of software. If you create a module that use its own table not available into default Dolibarr code, take a look at tutorial Module Development that will explain you how roviding new tables with your module to have the created during module activation and not during module usage.

Logs
Add logs to your code using function

Working directory
If you need to create a working directory, into your code, refer to it with DOL_DATA_ROOT.'/monmodule'

The directory can be created into your code by the following fnction:

If you need a directory to store temporary data, this directory must be 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.

Patterns of code organization
Martin Fowler has identified 3 ways to organize code: This is the old school used by all procedural languages. Inconvenient: Redundancy of code. Need to know the physical model of data to develop. 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. This is a mix between 2 previous where we have only one unique class for each table of database.
 * The Transaction Script (The source code is linear for each user action).
 * The Domain Model
 * The Table Module

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

Communication between business logic - data (ORM)
There is 3 way to make links:

This is the most simple. You have one class per table and each class is a link to the table with CRUD methods (Ceate, Read, Update, Delete). A class instance is a record a the table. The class contains only code to reach lines and fields of table.
 * The Table And Row Data Gateway

Example: This mode is used by some ORM Frameworks, like iBatis (http://ibatis.apache.org/).

Same than previous, but we are allowed to add some business functions into the class, if such functions are dedicated to the table or recordng into this table.
 * The Active Record

Example: This mode is used for Dolibarr development and most PHP softwares that includes their own framework and best practices.

Classes represent entities of the problem and not the data. So you have to double, triple ... theese classes with Mapper classes to access the data. More "purist" on paper (closer of business), this method also has the disadvantage of being more complex in practice.
 * The Data Mapper

Example: This is the choice if you use the ORM Framework Propel (http://propel.phpdb.org/trac/). We find this model on heavier applications, based on this ORM among others.

-> For Dolibarr development, it is recommended to use the connection mode Active Record, which offers the advantages of a model close to the business without having the complexity, without obfuscating the technique. It is in this way that the development, understanding of code and technical maintenance and / or business seems the more productive (this is however an ongoing debate between the purists and the pragmatists, debate in which nobody can really be right, because it depends on the objectives).