Normes, sécurité et meilleures pratiques

Référence sur drupal.org : 17 Janvier 2009 – 00h32 - http://drupal.org/node/360052


Cette section expose les normes Drupal pour l’écriture du code et le maintien de la sécurité.  Elle examine également un certain nombre de bonnes pratiques qui vous aideront à optimiser la vitesse et les performances de votre application.

Normes de programmation

Référence sur drupal.org : 8 Juin 2009 – 15h51 - http://drupal.org/coding-standards


Remarque : les Normes de programmation de Drupal s’appliquent au code de Drupal et à celui des modules tiers. Ce document est basé sur les normes de codage PEAR. Les commentaires et les noms devraient utiliser l’orthographe anglo-américaine (« color » et non pas « colour », etc).

Contenu de la page

Indentation

Utilisez une indentation de 2 espaces, pas de tabulations.

Pas d’espace après le dernier mot de la ligne.

Les lignes doivent être formatées avec un \n en fin de ligne (fins de ligne Unix), pas de \r\n (fins de lignes Windows).

Opérateurs

Tous les opérateurs tels que +, -, =, !=, ==, >,  etc, doivent avoir une espace avant et après l’opérateur, pour améliorer la lisibilité.

Par exemple, une affectation doit être formatée $foo = $bar; au lieu de $foo=$bar;.

Structures de contrôle

Cela concerne if, for, while, switch, etc. Voici un exemple de déclaration IF, puisque c’est une des plus complexe :

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

Les déclaration de structures de contrôle devraient avoir une espace entre le mot-clé et la parenthèse ouvrante, afin de les distinguer des appels de fonctions.

Vous êtes fortement encouragés à toujours utiliser les accolades, même dans les cas où elles seraient techniquement facultatives. Ceci accroît la lisibilité et réduit les risques d’erreurs de logique qui peuvent apparaître avec l’ajout de nouvelles lignes.

Pour les déclarations switch :

switch ($nomdevariable) {
  case 1:
    action1;
    break;

  case 2:
    action2;
    break;

  default:
    defaultaction;
}

Appels de fonctions

Les fonctions devraient être appelées sans espace entre le nom de la fonction, la parenthèse ouvrante, et le premier paramètre; espaces entre les virgules et chaque paramètre, la parenthèse fermante, le point-virgule. Voici un exemple :

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

Comme affiché plus haut, il devrait y avoir une espace de part et d’autre du signe égal utilisé pour affecter la valeur de retour de la fonction à la variable. Dans le cas de blocs comportant plusieurs affectations, plus d’espaces peuvent être insérées afin d’accroître la lisbilité :

$short         = foo($bar);
$long_variable = foo($baz);

Déclaration de fonctions

function funstuff_system($field) {
  $system["description"] = t("Ce module insère du texte rigolo dans les publications, au hasard.");
  return $system[$field];
}

Les arguments ayant des valeurs par défaut se trouvent en dernières positions de la liste d’arguments. Tentez toujours de retourner une valeur d’une fonction s’il en existe d’adéquate.

Tableaux

Les tableaux devraient être formatés avec une espace séparant chaque élément et opérateur d’affectation, si le tableau le permet :

$some_array = array('hello', 'world', 'foo' => 'bar');

Notez que si la ligne dépasse 80 caractères (ce qui est souvent le cas pour la déclaration  d’un formulaire ou d’un menu) chaque élément devrait être disposé dans sa propre ligne, et indenter d’un niveau :

$form['title'] = array(
  '#type' => 'textfield',
  '#title' => t('Titre'),
  '#size' => 60,
  '#maxlength' => 128,
  '#description' => t('Le titre de votre node.'),
);

Notez la virgule après les dernier élément du tableau. Ce n’est pas de la typographie ! Cela évite des erreurs de parsing si un autre élément est ultérieurement placé en fin de liste.

Guillemets

Les guillemets simples et doubles sont des caractéristiques de PHP pour faciliter la programmation et il n’y a pas de raisons qui feraient préférer les uns ou les autres. Cette pratique est laissée à l’appréciation du programmeur. Quand c’est possible, soyez cohérents dans tous vos modules et respectez le style des autres développeurs.

Avec cette mise en garde : les guillemets simples sont connus pour être plus rapides puisque le parser n’a pas à recherche des variables dedans. Leur utilisation est recommandée sauf dans deux cas :

1.utilisation de variables à l’intérieur des guillemets

2.Chaînes traduites où l’on peut éviter l’échappement des guillemets simples en encadrant la chaîne de doubles guillemets. Une chaîne de ce type « C’est un bon gars » deviendrait ’C\’est un bon gars » avec des guillemets simples. Ce type d’échappement pourrait ne pas être correctement géré par les générateurs de fichiers de traductions .pot.

Concaténation de chaînes

Pour améliorer la lisibilité, utilisez toujours une espace entre le point et les chaînes à concaténer :

<?php
  $string 
'Foo' $bar;
  
$string $bar 'foo';
  
$string bar() . 'foo';
  
$string 'foo' 'bar';
?>

Quand vous concaténez des variables simples, vous pouvez utiliser de guillemets doubles et ajouter la chaîne dedans, sinon utilisez des guillemets simples.

<?php
  $string 
"Foo $bar";
?>

Quand vous utilisez l’opérateur de concaténation .=, utilisez une espace de part et d’autre, comme pour le signe égal :

<?php
$string 
.= 'Foo';
$string .= $bar;
$string .= baz();
?>

Commentaires

La documentation au sein du code-source devrait respecter les normes de formatage Doxygen.

Les commentaires divers sont fortement encouragés. Une règle générale dit que si vous regardez une portion de code et vous exclamez « La vache ! Je ne veux pas essayer d’expliquer ça ! », alors vous devez commenter le code avant d’oublier comment il marche.

Les commentaires divers devraient utiliser des phrases avec des majuscules et ponctuées. Les phrases devraient être séparées par des espaces simples. Les lettres ne doivent être en capitales que pour désigner les constantes, par exemple : TRUE. Les commentaires devraient être sur une ligne à part juste avant la ligne de code ou le bloc de code qu’ils désignent. Par exemple :

// Unselect all other contact categories.
db_query('UPDATE {contact} SET selected = 0');

Si chaque ligne d’une liste a besoin d’un commentaire, ils peuvent être placés sur la même ligne et indentés de façon uniforme pour une meilleure lisibilité.

Les commentaires comme en C /* */ et en C++ // sont valables. L’utilisation de commentaires à la façon Perl/shell # sont déconseillés.

Inclusion de code

Quel que soit l’endroit où vous incluez un fichier de classe inconditionnellement, utilisez require_once().

Quel que soit l’endroit où vous incluez un fichier de classes conditionnellement (par exemple, pour des factory methods), utilisez include_once().

Chacune de ces instructions fera en sorte que les fichiers classes ne soient inclus qu’une fois. Elles partagent la même liste de fichiers ce qui fait que vous pouvez les mélanger sans crainte – un fichier inclus avec require_once() ne sera pas à nouveau inclus avec include_once().

Remarque : include_once() et require_once() dont des instructions, pas des fonctions. Vous n’avez pas besoin des parenthèses autour du nom de fichier à inclure.

Lorsque vous incluez du code du même dossier ou d’un sous-dossier, commencez le chemin du fichier avec . :

include_once ./includes/mymodule_formatting.inc

Dans Drupal 7.x et ultérieur, utilisez DRUPAL_ROOT :

require_once DRUPAL_ROOT . '/' . variable_get('cache_inc', 'includes/cache.inc');

Balises PHP

Utilisez toujours <?php ?> pour délimiter le code PHP, pas le raccourci <? ?>. C’est exigé  pour la conformité avec Drupal et c’est aussi la façon la plus portable d’inclure du code PHP sur différents systèmes d’exploitation ou configurations.

Notez qu’à compter de Drupal 4.7, le ?> à la fin du code est volontairement omis. Cela concerne les modules et les fichiers inclus. En voici brièvement les raisons :

Points-virgules

Le langage PHP exige la présence de points-virgules à la fin de la plupart des lignes mais permet leur omission à la fin d’un bloc de code.

Les normes de programmation Drupal les exigent, même à la fin d’un bloc de code. En particulier pour les blocs d’une seule ligne :


<?php print $tax?> -- YES
<?php print $tax ?> -- NO
?>

En-tête CVS

Tout code source du core de Drupal devrait contenir le bloc de commentaire suivant en en-tête :

<?php
// $Id$

Cette étiquette sera complétée avec des infos utiles par le CVS :

<?php
// $Id: CODING_STANDARDS.html,v 1.15 2008/12/22 15:27:26 keithsmith Exp $

Modèles d’URL

Utilisez « example.com » pour toutes les exemples d’URL, comme expliqué dans RFC 2006.

Conventions de nommage

Fonctions

Les noms de fonctions doivent être en minuscules et les mots séparés par un signe souligné. De plus, le nom du module doit préfixer le nom, pour éviter les conflits entre les modules.

Les membres de classes privées (c’est à dire les membres de classes destinées à n’être utilisées que dans la classe où ils sont déclarés) sont précédés par un signe souligné unique. Par exemple :

_node_get()

$this->_status

Constantes

Les constantes doivent toujours êtres écrites en majuscules, avec un signe souligné » séparant les mots. Cela concerne les constantes pré-déclarées de PHP, comme TRU, FLASE et NULL. Les constantes déclarées dans les modules doivent aussi être préfixées par le nom du module où elles sont déclarées, en majuscules.

Variables globales

Si vous avez besoin de définir des variables globales, leur nom doit commencer par un signe souligné unique, suivi du nom du thème ou du module et d’un autre signe souligné.

Noms des classes

Les classes doivent être nommées en utilisant le style CamelCase. Par exemple :

<?php
abstract class DatabaseConnection extends PDO {
?>

Les méthodes de classe et les propriétés doivent utiliser le style lowerCamelCase

<?php
public $lastStatement;
?>

Noms de fichiers

Tous les fichiers de documentation doivent porter l’extension .txt pour pouvoir les repérer facilement sur un système Windows. De même, les noms de ces fichiers doivent être entièrement en capitales (par exemple : README.txt au lieu de readme.txt), tandis que l’extension doit toujours être en minuscules (c’est à dire .txt au lieu de .TXT).

Exemples : README.txt, INSTALL.txt, TODO.txt, CHANGELOG.txt etc.

Script d’aide

Drupal dispose d’un script en ligne de commande pour vérifier si votre code satisfait aux normes de codage. Le fichier code-style.pl est situé dans le dossiers /scripts. Pour l’utiliser, indiquez simplement votre fichier en argument (assurez-vous que le script soit exécutable :

./code-style.pl path/to/file/example.module

Vous obtiendrez une liste de propositions sur les endroits de votre code à améliorer. Vous devrez effectuer ces modifications vous-même.

Module d’aide

Il existe un module qui vous assiste dans les révisions du code. Pour l’utiliser, effectuez les opérations suivantes :

Conventions de formatage Doxygen

Conventions de formatage Doxygen

Référence sur drupal.org : 28 Mai 2009 – 14h16 - http://drupal.org/node/1354


Doxygen est un logiciel permettant de créer une documentation. Les informations sont directement puisées dans le code-source, ce qui en rend la maintenance plus aisée et plus conforme avec le code-source.

Le site Doxygen fournit un excellent manuel de l’utilisateur. Les notes qui suivent concernent l’implémentation de Doxygen dans Drupal.

Syntaxe générale pour la documentation

Pour documenter un bloc de code, la syntaxe à utiliser est la suivante :

 * Documentation here.
 */

Doxygen analysera tout commentaire placé dans ce type de bloc. Notre but est d’utiliser le moins de commandes Doxygen possibles, pour préserver la lisibilité du code. Toute mention de fonctions ou de fichiers dans la documentation sera automatiquement liée au code correspondant, il sera donc inutile de présenter les balises qui génèrent les liens.

Documenter les fichiers

Il est recommandé de de décrire, en début de chaque fichier, ce qu’il fait. Par exemple :

<?php
// $Id: theme.inc,v 1.202 2004/07/08 16:08:21 dries Exp $

/**
* @file
* The theme system, which controls the output of Drupal.
*
* The theme system allows for nearly all output of the Drupal system to be
* customized by user themes.
*/

La ligne se trouvant juste après la directive @file est une brève description qui sera affichée dans la liste des fichiers de la documentation. Si la ligne commence par un verbe, il devra être au présent (par exemple : « Handles files uploads »). Une description plus détaillée peut se trouver après une ligne vierge.

Pour ajouter à votre fichier la balise ID du CVS, ajoutez un // $Id$ à votre fichier. CVS le complétera automatiquement au format indiqué précédemment. Dans le futur, vous n’aurez plus à vous en préoccuper car CVS actualisera automatiquement cette information.

Pour les fichiers .install, le modèle suivant est utilisé :

/**
 * @file
 * Install, update and uninstall functions for the XXX module.
 */

Documenter les fonctions

Toutes les fonctions susceptibles d’être appelées par d’autres fichiers devraient être documentées; les fonctions privées peuvent également être documentées. Un bloc de documentation de fonction devrait immédiatement précéder la déclaration de fonction elle-même, comme ceci :

/**
 * Verify the syntax of the given e-mail address.
 *
 * Empty e-mail addresses are allowed. See RFC 2822 for details.
 *
 * @param $mail
 *   A string containing an email address.
 * @return
 *   TRUE if the address is in a valid format.
 */
function valid_email_address($mail) {

La première ligne du bloc devrait contenir une brève description de ce que fait la fonction, elle débutera par un verbe sous la forme « Do such and such » (plutôt que « Does such and such »). Une description plus longue, comprenant des indications d’utilisation peut être ajoutée, en laissant une ligne vierge avant.

Les paramètres seront mentionnés avec la directive @param, avec leur description dans la ligne d’après. Après tous les paramètres, une directive @return doit être utilisée pour indiquer la valeur retournée par la fonction, si elle existe. Il n’y a pas de ligne vierge entre les directive @param et @return.

Les fonctions qui peuvent être décrites en une seule ligne peuvent omettre ces directives, comme ci-dessous :

/**
 * Convert an associative array to an anonymous object.
 */
function array2object($array) {

Les paramètres et la valeur de retour doivent être décrits dans cette seule ligne de description.

Documenter l’implémentation des hooks

De nombreux modules sont constitués d’implémentation de hooks. Si l’implémentation est plutôt standard et n’exige pas plus d’explications que celles fournies par la référence au hook, une courte documentation peut être utilisée :

/**
 * Implement hook_help().
 */
function blog_help($section) {

Ceci génère un lien vers la référence du hook, rappelle au développeur qu’il s’agit d’une implémentation de hook, et évite d’avoir à documenter les paramètres et les valeurs de retour, qui sont les mêmes pour chaque implémentation du hook.

Documenter les formulaires

Pour fournir un guide de référence sommaire aux concepteurs de thèmes, on balise chacune des fonction qui construisent les formulaires, ainsi Doxygen peut les regrouper ensemble.

Une fonction de construction de formulaire est une fonction pouvant être utilisée comme argument pour drupal_get_form. Pour ce faire, ajoutez une instruction de regroupement à la documentation de cette fonction. De plus, alors que submit, validate et autre gestionnaires du formulaire ne sont pas destinés à figurer dans le groupe, vous devrez fournir un @see pour fournir une référence aux gestionnaires rattachés à ce formulaire.

/**
 * FAPI definition for the user login form.
 *
 * ...
 * @ingroup forms
 * @see user_login_default_validators()
 * @see user_login_submit()
 */
function user_login(&$form_state, $msg = '')

Documenter les fonctions de thèmes

Pour fournir un guide de référence sommaire aux développeurs de thèmes, on balise toutes les fonctions « thémables » pour que Doxygen puisse les regrouper sur une seule page. Pour ce faire, ajoutez une instruction de regroupement à la documentation de ces fonctions :

/**
 * Format a query pager.
 *
 * ...
 * @ingroup themeable
 */
function theme_pager($tags = array(), $limit = 10, $element = 0, $attributes = array()) {
  ...
}

Documenter les gabarits de thème

Si une gabarit et une fonction de pré-traitement est utilisé à la place d’une fonction de thème, une déclaration de fonction vide pour la fonction de thèe qui n’est pas utilisée doit être placée dans la documentation contributive (contributions/docs/developer/theme.php).

Le gabarit lui-même doit être documentée avec la directive @file et doit contenir la liste des variables que template_preprocess_HOOK lui a préparé.

Si l’une de ces variables contient des données dont il est, pour x raisons, risqué d’afficher la valeur, elles doivent être documentées; sinon, on suppose que les variables disponibles sont correctement filtrées. Ce qui n’est pas listé ne doit pas être considéré comme sûr pour l’affichage.

La directive @see doit également être mentionnée pour un lien vers les fonctions de pré-traitement et les fonctions theme_X

<?php
// $Id$

/**
* @file
* Default theme implementation to display a list of forums.
*
* Available variables:
* - $forums: An array of forums to display.
*
* Each $forum in $forums contains:
* - $forum->is_container: Is TRUE if the forum can contain other forums. Is
 *   FALSE if the forum can contain only topics.
 * - $forum->depth: How deep the forum is in the current hierarchy.
 * - $forum->name: The name of the forum.
 * - $forum->link: The URL to link to this forum.
 * - $forum->description: The description of this forum.
 * - $forum->new_topics: True if the forum contains unread posts.
 * - $forum->new_url: A URL to the forum's unread posts.
 * - $forum->new_text: Text for the above URL which tells how many new posts.
 * - $forum->old_topics: A count of posts that have already been read.
 * - $forum->num_posts: The total number of posts in the forum.
 * - $forum->last_reply: Text representing the last time a forum was posted
 *   or commented in.
 *
 * @see template_preprocess_forum_list()
 */

La fonction template_preprocess_HOOK doit également contenir les directives @see adéquates.

Documenter les modules contributifs et les thèmes

  • N’utilisez pas @mainpage. Il ne peut y avoir qu’un seul @mainpage dans le dépôt des contributions et il est réservé pour la page qui indexe toutes les modules tiers et les thèmes.
  • Utilisez Doxygen Modules (@defgroup, @ingroup, @addtogroup, voir « Limites et conseils ci-dessous) raisonnablement. Il y a actuellement plus de 2 200 dossiers de modules dans contrib, la plupart d’entre eux comprennent plus d’un module. Si chacun de ces modules n’utilise qu’un seul @defgroup, il y aura plus de 2 200 entrées dans la liste des Modules. Si chacun d’eux en utilise plus d’un...
  • Si vous utilisez Doxygen Modules, assurez-vous que vous ne leur donnez qu’un seule namespace, qui devrait être le nom de votre module. Par exemple : @defgroup views … pour le module Views, @defgroup views_ui … pour le module Views_UI. N’utilisez pas les noms de groupes définis dans le core de Drupal (hooks, themeable, file, batch, database, forms, form_api, format, image, validation, search, etc.).

Une façon de faire conseillée pour l’utilisation de regroupement Doxygen dans les modules tiers et les thèmes est la suivante :

Dans votre example.module principal :

/**
 * @defgroup example Example: short description of your module
 *
 * Longer description of your module, including all other files and modules.
 */
 
/**
 * @file
 *
 * Description of your module's main file.
 *
 * @ingroup example
 */

Dans les autres modules et fichiers de code de votre module :

/**
 * @file
 *
 * Description of another file in your module.
 *
 * @ingroup example
 */

De cette façon, vous avez tous les fichiers du module présentés ensemble dans le groupe  « example ».

Limites et conseils

Le module de traitement Doxygen de Drupal, api.module, ne supporte actuellement qu’un petit ensemble des commandes Doxygen et s'attend à quelque formatage du code-source. Le code à faire traiter par l’api.module doit se conformer à ces conventions.

L’api.module ne supporte actuellement que l’un des trois mécanismes de regroupement de Doxygen : Modules (@defgroup, @ingroup, @addtogroup, @{, @}). Lorsque vous les utilisez, notez ce qui suit :

  • Modules travaille à un niveau global, en créant une nouvelle page pour chaque groupe. Il ne devrait être utilisé que pour regrouper des fonction qui fournissent une sorte d’API, qui peuvent éventuellement comprendre plusieurs fichiers. A l’inverse : il ne devrait pas être utilisé pour regrouper des fonctions dans un fichier lorsque ces fonctions ne sont utilisées que dans ce même fichier. c’est le rôle de Member Groups (qui, malheureusement, n’est pas encore supporté par api.module).
  • @defgroups ne peut être déclaré qu’une fois. Déclarer un deuxième @defgroup name avec un nom déjà utilisé causera une erreur. Utilisez @defgroup dans la section la plus importante du fichier et @addtogroup / @ingroup pour l’ajout depuis d’autres endroits du fichier.
  • Le name dans @defgroup name Explaination of that group doit être un identifiant d’un mot seulement, comme l’est le nom d’une variable ou d’une fonction en PHP. Ou, en tant qu’expression rationnelle : [a-zA-Z_][a-zA-Z0-9_]*. Les points, tirets, etc, ne sont pas autorisés.

Pour voir Doxygen à l’œuvre sur la documentation du core Drupal, jetez un œil à ax' Drupal site. Plus particulièrement, regardez les « logs d’erreurs doxygen » et donnez un coup de main en améliorant la documentation du core Drupal.

CSS : normes de programmation

Référence sur drupal.org : 30 Avril 2009 – 08h55 - http://drupal.org/node/302199


Cet article est une suggestion. Voir la discussion sur http://groups.drupal.org/node/14421

Remarque : les normes de codage CSS pour Drupal s’appliquent au code faisant partie du core ou des modules tiers. Ce document se base sur les normes initialement proposées par Nick Lewis

Les discussions sur ces normes se poursuivent ici :  http://groups.drupal.org/node/14421

Ecrire des CSS valides

Tout le code CSS  devrait être répondre aux normes, de préférence à la norme CSS 2.1, mais la norme CSS 3.0 est  acceptable à condition que son utilisation soit justifiée et qu’il puisse y être dérogé sans altération majeure de la présentations.

Sélecteurs

Les sélecteurs doivent être sur une seule ligne, avec une espace après le dernier sélecteur, suivi par une accolade. Un sélecteur doit se terminer par une accolade fermante, non indentée, sur une ligne séparée.

.book-navigation .page-previous {
}

Sélecteurs multiples

En cas de sélecteurs multiples, ils doivent figurer chacun sur une seule ligne, sans espaces après la virgule.

#forum td.posts,
#forum td.topics,
#forum td.replies,
#forum td.pager {

Propriétés

Toutes les propriétés doivent se trouver sur la ligne qui suit l’accolade.

Chaque propriété doit :

  • être sur sa propre ligne
  • être indentée avec deux espaces
  • avoir une espace après le nom de la propriété et un espace avant la valeur de la propriété
  • se terminer par un point-virgule
#forum .description {
  color: #EFEFEF;
  font-size: 0.9em;
  margin: 0.5em;
}

Ordre alphabétique des propriétés

Les propriétés doivent être classées par ordre alphabétique. Plutôt que d’avoir ceci

body {
  font-weight: normal;
  background: #000;
  font-family: helvetica, sans-serif;
  color: #FFF;
}

vous devriez avoir cela

body {
  background: #000;
  color: #FFF;
  font-family: helvetica, sans-serif;
  font-weight: normal;
}

Propriétés avec des valeurs multiples

Lorsque des propriétés peuvent avoir des valeurs multiples, chacune de ces valeurs doit être séparé par une espace :

  font-family: helvetica, sans-serif;

Commentaires

Les fichiers CSS devraient être annotés en utilisant la syntaxe CSSdoc. Comme pour les normes de codage PHP, un bloc de documentation doit être utilisé comme ci-dessous, pour décrire la section de code qui suit le commentaire :

/**
* Documentation here.
*/

Une balise CVS ID et un bloc CSSdoc de version de fichier devraient être placés au début du fichier pour décrire le contenu du fichier :

/* $Id$ */

/**
* @file
* Short description
*
* Long description
*/

La balise CVS ID sera complétée par le CVS avec les informations adéquates

/* $Id: style.css,v 1.15 2008/12/22 15:27:26 keithsmith Exp $ */

De brefs commentaires peuvent être ajoutés après une propriété, précédés par une espace :

  background-position: 2px 2px; /* LTR */
  padding-left: 25px; /* LTR */

RTL

Drupal supporte le chargement conditionnel des fichiers CSS comportant des règles pour les langues s’écrivant de droite à gauche.

Pour un module, la surcharge de la règle doit se trouver dans un fichier nommé MODULE-rtl.css, par exemple node-rtl.css.

Pour un thème, la règle qui est surchargée doit être commentée dans la règle CSS par défaut.

Dans node-rtl.css:

#node-admin-buttons {
  clear: left;
  float: right;
  margin-left: 0;
  margin-right: 0.5em;
}

La règle dans node.css sera surchargée si le fichier rtl.css est chargé :

#node-admin-buttons {
  clear: right; /* LTR */
  float: left; /* LTR */
  margin-left: 0.5em; /* LTR */
}

Voir aussi http://drupal.org/node/132442#language-rtl.

En règle générale, vous devriez ajouter un commentaire /* LTR */ dans votre feuille de style :

  • Lorsque vous utilisez les mots-clé left ou right dans une propriété, par exemple : float : left;
  • Quand vous utilisez des marges, paddings ou bordures non symétriques sur les côtés d’une boite, par exemple :
    margin-left: 1em;
    margin-right: 2em;
  • Quand vous indiquez le sens de lecture, par exemple direction: ltr;.

Blocs de commentaires en en-tête

Référence sur Drupal.org : 25 Octobre 2008 – 08h13 - http://drupal.org/node/546


Tout le code-source de Drupal doit débuter par un en-tête contenant le mot-clé $Id$.

<?php
// $Id: CODING_STANDARDS,v 1.1 2001/11/05 07:32:17 natrak Exp $

Si vous ajoutez un nouveau fichier au CVS, écrivez simplement

<?php
// $Id$

Copiez cette ligne 1.1. Notez que :

  • La balise CVS ID doit toujours se trouver sur la première ligne de chaque fichier (pour ce qui est des scripts PHP, il s’agit en fait de la deuxième ligne)
  • Chaque fois que vous faites un checkout ou que vous actualisez le workspace, CVS remplace automatiquement tout ce qui se trouve après $Id et avant le signe $ fermant par les valeurs adéquates. Vous n’avez pas à modifier cette partie manuellement, et vous n’avez pas à vous préoccuper des modifications de cette ligne qui pourraient créer des conflits lorsque vous validez.
  • La balise CVS ID est sensible à la casse.
  • Vous ne devez pas ajouter d’espaces entre $Id et $ lorsque vous « committez » le mot-clé pour la première fois.

Autres fichiers

Fichiers CSS

doivent contenir la balise CVS ID sur la première ligne utilisant un commentaire CSS correct.

/* $Id$ */

Attention : avec certains navigateurs, utiliser un commentaire ou une syntaxe  de commentaire incorrects peut casser toute la feuille de style.

Fichiers JavaScript

doivent contenir la balise CVS ID sur la première ligne utilisant un commentaire JS correct.

// $Id$

Fichier .info

doivent contenir la balise CVS ID sur la première ligne, précédée d’un point-virgule

; $Id$

Fichiers Texte

comme README.txt ou CHANGELOG.txt, doivent contenir la balise CVS ID sur la première ligne, en utilisant l’une de ces notations :

/* $Id$ */
# $Id$

Notez que si votre copie locale ne complète pas ces mots-clé, il est possible que votre client CVS soit mal paramétré, ou que quelqu’un avec un client CVS mal paramétré ait fait un check-in du fichier, avec un flag qui désactive la substitution du mot-clé. 

Vous pouvez utiliser cvs status et chercher la ligne Sticky options. Si vous voyez -ko ou -kb, cela signifie que la substitution de mot-clé a été désactivée pour ce fichier. Dans ce cas, vous pouvez exécuter cvs admin -kkv [nomdefichier] pour rétablir la substitution. Si vous utiliser une interface graphique pour CVS, y trouver la façon de spécifier ce flag est laissé au soin du lecteur...

SQL : conventions de programmation

Référence sur drupal.org : 14 Mars 2009 – 22h26 - http://drupal.org/node/2497


N’utilisez pas de mots-clé réservés

N’utilisez pas les mots-clé de (ANSI) SQL / MySQL / PostgreSQL / MS SQL Server / …, les mots-clés pour les noms de colonnes, tables. Même si ça marche bien avec votre installation de xxSQL, ça peut ne pas marcher du tout avec d’autres, ou avec d’autres bases de données.

Quelques références :

Quelques mots-clés fréquemment mal-employés : TIMESTAMP, TYPE, TYPES, MODULE, DATA, DATE, TIME, ...

Voir aussi [bug] SQL Reserved Words.

Mise en capitales et données fournies par l’utilisateur

  • Écrivez les mots-clé SQL en CAPITALES. Ce n’est pas une suggestion. La couche d’abstraction DB de Drupal échouera si cette convention n’est pas appliquée.
  • Écrivez les noms des colonnes et des contraintes en minuscules
  • Entourez chaque nom de table par des accolades {} (cela permet à Drupal de préfixer le nom des tables).
  • Les arguments variables (qui sont souvent fournis par l’utilisateur) ne doivent pas se trouver dans le corps de la requête mais passés en tant que paramètres séparés à db_query(), db_query_range(), et db_query_temporary(), etc.
    A la place, le corps de la requête doit contenir des paramètres substituables (placeholders) en précisant le type de l’argument (<?php%d|%s|%%|%f|%b?>). Cela garantit que les données seront correctement échappées et évite les attaques par injection de code SQL.
  • Éviter les attaques par injection de code SQL est facile : db_query fournit une façon  d’utiliser les requêtes paramétrées. Les fonction de bases de données de Drupal remplace les paramètres avec les arguments correctement echappés, dans l’ordre de passage :
    • %d – entiers
    • %f – flottants
    • %s – chaînes, entourées de ’’
    • %b  - données binaires, ne pas encadrer de guillemets
    • %% - remplacé par %
  • Consultez Database Access
  • Les arguments littéraux (constantes) peuvent être soit inclus dans le corps de la requête, soit passés comme arguments.
  • Toute chaîne littérale ou paramètre de type %s doit être encadré par des guillemets simples : ' . N’utilisez jamais de guillemets doubles.

Exemple :


<?php
db_query
("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, 'php', 0, 0)"$format);
?>

Remarque : depuis Drupal 6.x, les déclarations de tables et de contraintes (comme les clés primaires, clés uniques, indexes) doivent toujours être manipulés par Schema API, qui résout automatiquement les questions de compatibilité inter-bases de données.

Nommage

  • Pour les noms de tables, utilisez des nom au singulier car ils décrivent l’entité que représente la table. Drupal 6.x utilisait autant le singulier que le pluriel et cette convention a été modifiée pour Drupal 7.x.
  • Nommez chaque contrainte vous-même (primaire, clé étrangère, clé unique). Sinon, dans les messages d’erreurs,  vous risquez de voir des noms bizarres générés par le système. Ceci s’est produit avec la table moderation_roles qui avait défini une clé sans nom en tant que KEY(mid). Ce qui, lors d’un dump, a donné KEY mid (mid), provoquant une erreur de syntaxe puisque mid() est une fonction mysql (voir [bug] mysql --ansi cannot import install database).
  • Les noms d’index doivent commencer par le nom de la table à laquelle ils s’appliquent. Par exempel : INDEX users_sid_idx.

Remarque : depuis Drupal 6.x, les déclarations de table et de contraintes doivent toujours être gérées par l’API Schema.

Configurez votre serveur de base de données pour le respect des normes

Beaucoup de serveurs de bases de données utilisent une extension pour le SQL standard. Cependant, plusieurs d’entre eux peuvent être configurés pour s'exécuter dans plus d’un standard. Chaque développeur est encouragé à utiliser le mode le plus conforme à la norme pour éviter le codage fantaisiste et des problèmes de compatibilité.

MySQL

Activez les mode ANSI et Strict.

Merci de nous aider à compléter cette liste de serveur de bases de données !

Références

Indentation

Drupal n’a pas de méthodes normalisée pour l’indentation ou la mise en forme de requêtes SQL longues sur plusieurs lignes. Parmi ces méthodes, on a :


<?php
if (!(db_query(
  
"
    INSERT INTO {mlsp_media_file_type}
    SET extension   = '%s',
    attributes      = '%s'
  "
,
  
$file_type_entry['extension'],
  
$selected_attributes
))) {
  
$errors TRUE;
}
?>

ou


<?php
$sql 
"SELECT t.*, j1.col1, j2.col2"
" FROM {table} AS t"
" LEFT JOIN {join1} AS j1 ON j1.id = t.jid"
" LEFT JOIN {join2} AS j2 ON j2.id = t.jjid"
" WHERE t.col LIKE '%s'"
" ORDER BY %s"
;
$result db_query($sql'findme''t.weight');
?>

 

Evitez SELECT* FROM...

Référence sur drupal.org : 15 Février 2009 – 20h02 - http://drupal.org/node/374660


Evitez l’utilisation du caractère * dans les requêtes SELECT. Il est nettement préférable de spécifier les champs qui doivent être sélectionnés.

Avant Drupal 7.x, l’utilisation de requêtes comme SELECT * FROM {node} pouvait présenter un risque de sécurité car elles outrepassent le système d'accès aux nodes de Drupal (Drupal's Node Access). Dans ce cas de figure, du contenu confidentiel pouvait être affiché aux utilisateurs non autorisés. Par conséquent, les requêtes qui génèrent des liste de nodes doivent toujours éviter la syntaxe SELECT *.

Ce n’est pas encore une norme de programmation dans Drupal, mais un consensus est en train de se faire pour que les requêtes du style SELECT * FROM... ou SELECT p.*, b.* FROM... soient évitées.

Liste des mots SQL réservés

Référence sur Drupal.org : 3 Mai 2007 – 12h51 - http://drupal.org/node/141051


La liste ci-dessous regroupe les mots-clé SQL. Elle est établie à partir des sources suivantes :

Il y a bien évidemment d’autres sources qui pourraient êtres ajoutées à cette liste, mais elle n’en constitue pas moins un bon point de départ.

Mots réservés

  1. A
  2. ABORT
  3. ABS
  4. ABSOLUTE
  5. ACCESS
  6. ACTION
  7. ADA
  8. ADD
  9. ADMIN
  10. AFTER
  11. AGGREGATE
  12. ALIAS
  13. ALL
  14. ALLOCATE
  15. ALSO
  16. ALTER
  17. ALWAYS
  18. ANALYSE
  19. ANALYZE
  20. AND
  21. ANY
  22. ARE
  23. ARRAY
  24. AS
  25. ASC
  26. ASENSITIVE
  27. ASSERTION
  28. ASSIGNMENT
  29. ASYMMETRIC
  30. AT
  31. ATOMIC
  32. ATTRIBUTE
  33. ATTRIBUTES
  34. AUDIT
  35. AUTHORIZATION
  36. AUTO_INCREMENT
  37. AVG
  38. AVG_ROW_LENGTH
  39. BACKUP
  40. BACKWARD
  41. BEFORE
  42. BEGIN
  43. BERNOULLI
  44. BETWEEN
  45. BIGINT
  46. BINARY
  47. BIT
  48. BIT_LENGTH
  49. BITVAR
  50. BLOB
  51. BOOL
  52. BOOLEAN
  53. BOTH
  54. BREADTH
  55. BREAK
  56. BROWSE
  57. BULK
  58. BY
  59. C
  60. CACHE
  61. CALL
  62. CALLED
  63. CARDINALITY
  64. CASCADE
  65. CASCADED
  66. CASE
  67. CAST
  68. CATALOG
  69. CATALOG_NAME
  70. CEIL
  71. CEILING
  72. CHAIN
  73. CHANGE
  74. CHAR
  75. CHAR_LENGTH
  76. CHARACTER
  77. CHARACTER_LENGTH
  78. CHARACTER_SET_CATALOG
  79. CHARACTER_SET_NAME
  80. CHARACTER_SET_SCHEMA
  81. CHARACTERISTICS
  82. CHARACTERS
  83. CHECK
  84. CHECKED
  85. CHECKPOINT
  86. CHECKSUM
  87. CLASS
  88. CLASS_ORIGIN
  89. CLOB
  90. CLOSE
  91. CLUSTER
  92. CLUSTERED
  93. COALESCE
  94. COBOL
  95. COLLATE
  96. COLLATION
  97. COLLATION_CATALOG
  98. COLLATION_NAME
  99. COLLATION_SCHEMA
  100. COLLECT
  101. COLUMN
  102. COLUMN_NAME
  103. COLUMNS
  104. COMMAND_FUNCTION
  105. COMMAND_FUNCTION_CODE
  106. COMMENT
  107. COMMIT
  108. COMMITTED
  109. COMPLETION
  110. COMPRESS
  111. COMPUTE
  112. CONDITION
  113. CONDITION_NUMBER
  114. CONNECT
  115. CONNECTION
  116. CONNECTION_NAME
  117. CONSTRAINT
  118. CONSTRAINT_CATALOG
  119. CONSTRAINT_NAME
  120. CONSTRAINT_SCHEMA
  121. CONSTRAINTS
  122. CONSTRUCTOR
  123. CONTAINS
  124. CONTAINSTABLE
  125. CONTINUE
  126. CONVERSION
  127. CONVERT
  128. COPY
  129. CORR
  130. CORRESPONDING
  131. COUNT
  132. COVAR_POP
  133. COVAR_SAMP
  134. CREATE
  135. CREATEDB
  136. CREATEROLE
  137. CREATEUSER
  138. CROSS
  139. CSV
  140. CUBE
  141. CUME_DIST
  142. CURRENT
  143. CURRENT_DATE
  144. CURRENT_DEFAULT_TRANSFORM_GROUP
  145. CURRENT_PATH
  146. CURRENT_ROLE
  147. CURRENT_TIME
  148. CURRENT_TIMESTAMP
  149. CURRENT_TRANSFORM_GROUP_FOR_TYPE
  150. CURRENT_USER
  151. CURSOR
  152. CURSOR_NAME
  153. CYCLE
  154. DATA
  155. DATABASE
  156. DATABASES
  157. DATE
  158. DATETIME
  159. DATETIME_INTERVAL_CODE
  160. DATETIME_INTERVAL_PRECISION
  161. DAY
  162. DAY_HOUR
  163. DAY_MICROSECOND
  164. DAY_MINUTE
  165. DAY_SECOND
  166. DAYOFMONTH
  167. DAYOFWEEK
  168. DAYOFYEAR
  169. DBCC
  170. DEALLOCATE
  171. DEC
  172. DECIMAL
  173. DECLARE
  174. DEFAULT
  175. DEFAULTS
  176. DEFERRABLE
  177. DEFERRED
  178. DEFINED
  179. DEFINER
  180. DEGREE
  181. DELAY_KEY_WRITE
  182. DELAYED
  183. DELETE
  184. DELIMITER
  185. DELIMITERS
  186. DENSE_RANK
  187. DENY
  188. DEPTH
  189. DEREF
  190. DERIVED
  191. DESC
  192. DESCRIBE
  193. DESCRIPTOR
  194. DESTROY
  195. DESTRUCTOR
  196. DETERMINISTIC
  197. DIAGNOSTICS
  198. DICTIONARY
  199. DISABLE
  200. DISCONNECT
  201. DISK
  202. DISPATCH
  203. DISTINCT
  204. DISTINCTROW
  205. DISTRIBUTED
  206. DIV
  207. DO
  208. DOMAIN
  209. DOUBLE
  210. DROP
  211. DUAL
  212. DUMMY
  213. DUMP
  214. DYNAMIC
  215. DYNAMIC_FUNCTION
  216. DYNAMIC_FUNCTION_CODE
  217. EACH
  218. ELEMENT
  219. ELSE
  220. ELSEIF
  221. ENABLE
  222. ENCLOSED
  223. ENCODING
  224. ENCRYPTED
  225. END
  226. END-EXEC
  227. ENUM
  228. EQUALS
  229. ERRLVL
  230. ESCAPE
  231. ESCAPED
  232. EVERY
  233. EXCEPT
  234. EXCEPTION
  235. EXCLUDE
  236. EXCLUDING
  237. EXCLUSIVE
  238. EXEC
  239. EXECUTE
  240. EXISTING
  241. EXISTS
  242. EXIT
  243. EXP
  244. EXPLAIN
  245. EXTERNAL
  246. EXTRACT
  247. FALSE
  248. FETCH
  249. FIELDS
  250. FILE
  251. FILLFACTOR
  252. FILTER
  253. FINAL
  254. FIRST
  255. FLOAT
  256. FLOAT4
  257. FLOAT8
  258. FLOOR
  259. FLUSH
  260. FOLLOWING
  261. FOR
  262. FORCE
  263. FOREIGN
  264. FORTRAN
  265. FORWARD
  266. FOUND
  267. FREE
  268. FREETEXT
  269. FREETEXTTABLE
  270. FREEZE
  271. FROM
  272. FULL
  273. FULLTEXT
  274. FUNCTION
  275. FUSION
  276. G
  277. GENERAL
  278. GENERATED
  279. GET
  280. GLOBAL
  281. GO
  282. GOTO
  283. GRANT
  284. GRANTED
  285. GRANTS
  286. GREATEST
  287. GROUP
  288. GROUPING
  289. HANDLER
  290. HAVING
  291. HEADER
  292. HEAP
  293. HIERARCHY
  294. HIGH_PRIORITY
  295. HOLD
  296. HOLDLOCK
  297. HOST
  298. HOSTS
  299. HOUR
  300. HOUR_MICROSECOND
  301. HOUR_MINUTE
  302. HOUR_SECOND
  303. IDENTIFIED
  304. IDENTITY
  305. IDENTITY_INSERT
  306. IDENTITYCOL
  307. IF
  308. IGNORE
  309. ILIKE
  310. IMMEDIATE
  311. IMMUTABLE
  312. IMPLEMENTATION
  313. IMPLICIT
  314. IN
  315. INCLUDE
  316. INCLUDING
  317. INCREMENT
  318. INDEX
  319. INDICATOR
  320. INFILE
  321. INFIX
  322. INHERIT
  323. INHERITS
  324. INITIAL
  325. INITIALIZE
  326. INITIALLY
  327. INNER
  328. INOUT
  329. INPUT
  330. INSENSITIVE
  331. INSERT
  332. INSERT_ID
  333. INSTANCE
  334. INSTANTIABLE
  335. INSTEAD
  336. INT
  337. INT1
  338. INT2
  339. INT3
  340. INT4
  341. INT8
  342. INTEGER
  343. INTERSECT
  344. INTERSECTION
  345. INTERVAL
  346. INTO
  347. INVOKER
  348. IS
  349. ISAM
  350. ISNULL
  351. ISOLATION
  352. ITERATE
  353. JOIN
  354. K
  355. KEY
  356. KEY_MEMBER
  357. KEY_TYPE
  358. KEYS
  359. KILL
  360. LANCOMPILER
  361. LANGUAGE
  362. LARGE
  363. LAST
  364. LAST_INSERT_ID
  365. LATERAL
  366. LEADING
  367. LEAST
  368. LEAVE
  369. LEFT
  370. LENGTH
  371. LESS
  372. LEVEL
  373. LIKE
  374. LIMIT
  375. LINENO
  376. LINES
  377. LISTEN
  378. LN
  379. LOAD
  380. LOCAL
  381. LOCALTIME
  382. LOCALTIMESTAMP
  383. LOCATION
  384. LOCATOR
  385. LOCK
  386. LOGIN
  387. LOGS
  388. LONG
  389. LONGBLOB
  390. LONGTEXT
  391. LOOP
  392. LOW_PRIORITY
  393. LOWER
  394. M
  395. MAP
  396. MATCH
  397. MATCHED
  398. MAX
  399. MAX_ROWS
  400. MAXEXTENTS
  401. MAXVALUE
  402. MEDIUMBLOB
  403. MEDIUMINT
  404. MEDIUMTEXT
  405. MEMBER
  406. MERGE
  407. MESSAGE_LENGTH
  408. MESSAGE_OCTET_LENGTH
  409. MESSAGE_TEXT
  410. METHOD
  411. MIDDLEINT
  412. MIN
  413. MIN_ROWS
  414. MINUS
  415. MINUTE
  416. MINUTE_MICROSECOND
  417. MINUTE_SECOND
  418. MINVALUE
  419. MLSLABEL
  420. MOD
  421. MODE
  422. MODIFIES
  423. MODIFY
  424. MODULE
  425. MONTH
  426. MONTHNAME
  427. MORE
  428. MOVE
  429. MULTISET
  430. MUMPS
  431. MYISAM
  432. NAME
  433. NAMES
  434. NATIONAL
  435. NATURAL
  436. NCHAR
  437. NCLOB
  438. NESTING
  439. NEW
  440. NEXT
  441. NO
  442. NO_WRITE_TO_BINLOG
  443. NOAUDIT
  444. NOCHECK
  445. NOCOMPRESS
  446. NOCREATEDB
  447. NOCREATEROLE
  448. NOCREATEUSER
  449. NOINHERIT
  450. NOLOGIN
  451. NONCLUSTERED
  452. NONE
  453. NORMALIZE
  454. NORMALIZED
  455. NOSUPERUSER
  456. NOT
  457. NOTHING
  458. NOTIFY
  459. NOTNULL
  460. NOWAIT
  461. NULL
  462. NULLABLE
  463. NULLIF
  464. NULLS
  465. NUMBER
  466. NUMERIC
  467. OBJECT
  468. OCTET_LENGTH
  469. OCTETS
  470. OF
  471. OFF
  472. OFFLINE
  473. OFFSET
  474. OFFSETS
  475. OIDS
  476. OLD
  477. ON
  478. ONLINE
  479. ONLY
  480. OPEN
  481. OPENDATASOURCE
  482. OPENQUERY
  483. OPENROWSET
  484. OPENXML
  485. OPERATION
  486. OPERATOR
  487. OPTIMIZE
  488. OPTION
  489. OPTIONALLY
  490. OPTIONS
  491. OR
  492. ORDER
  493. ORDERING
  494. ORDINALITY
  495. OTHERS
  496. OUT
  497. OUTER
  498. OUTFILE
  499. OUTPUT
  500. OVER
  501. OVERLAPS
  502. OVERLAY
  503. OVERRIDING
  504. OWNER
  505. PACK_KEYS
  506. PAD
  507. PARAMETER
  508. PARAMETER_MODE
  509. PARAMETER_NAME
  510. PARAMETER_ORDINAL_POSITION
  511. PARAMETER_SPECIFIC_CATALOG
  512. PARAMETER_SPECIFIC_NAME
  513. PARAMETER_SPECIFIC_SCHEMA
  514. PARAMETERS
  515. PARTIAL
  516. PARTITION
  517. PASCAL
  518. PASSWORD
  519. PATH
  520. PCTFREE
  521. PERCENT
  522. PERCENT_RANK
  523. PERCENTILE_CONT
  524. PERCENTILE_DISC
  525. PLACING
  526. PLAN
  527. PLI
  528. POSITION
  529. POSTFIX
  530. POWER
  531. PRECEDING
  532. PRECISION
  533. PREFIX
  534. PREORDER
  535. PREPARE
  536. PREPARED
  537. PRESERVE
  538. PRIMARY
  539. PRINT
  540. PRIOR
  541. PRIVILEGES
  542. PROC
  543. PROCEDURAL
  544. PROCEDURE
  545. PROCESS
  546. PROCESSLIST
  547. PUBLIC
  548. PURGE
  549. QUOTE
  550. RAID0
  551. RAISERROR
  552. RANGE
  553. RANK
  554. RAW
  555. READ
  556. READS
  557. READTEXT
  558. REAL
  559. RECHECK
  560. RECONFIGURE
  561. RECURSIVE
  562. REF
  563. REFERENCES
  564. REFERENCING
  565. REGEXP
  566. REGR_AVGX
  567. REGR_AVGY
  568. REGR_COUNT
  569. REGR_INTERCEPT
  570. REGR_R2
  571. REGR_SLOPE
  572. REGR_SXX
  573. REGR_SXY
  574. REGR_SYY
  575. REINDEX
  576. RELATIVE
  577. RELEASE
  578. RELOAD
  579. RENAME
  580. REPEAT
  581. REPEATABLE
  582. REPLACE
  583. REPLICATION
  584. REQUIRE
  585. RESET
  586. RESIGNAL
  587. RESOURCE
  588. RESTART
  589. RESTORE
  590. RESTRICT
  591. RESULT
  592. RETURN
  593. RETURNED_CARDINALITY
  594. RETURNED_LENGTH
  595. RETURNED_OCTET_LENGTH
  596. RETURNED_SQLSTATE
  597. RETURNS
  598. REVOKE
  599. RIGHT
  600. RLIKE
  601. ROLE
  602. ROLLBACK
  603. ROLLUP
  604. ROUTINE
  605. ROUTINE_CATALOG
  606. ROUTINE_NAME
  607. ROUTINE_SCHEMA
  608. ROW
  609. ROW_COUNT
  610. ROW_NUMBER
  611. ROWCOUNT
  612. ROWGUIDCOL
  613. ROWID
  614. ROWNUM
  615. ROWS
  616. RULE
  617. SAVE
  618. SAVEPOINT
  619. SCALE
  620. SCHEMA
  621. SCHEMA_NAME
  622. SCHEMAS
  623. SCOPE
  624. SCOPE_CATALOG
  625. SCOPE_NAME
  626. SCOPE_SCHEMA
  627. SCROLL
  628. SEARCH
  629. SECOND
  630. SECOND_MICROSECOND
  631. SECTION
  632. SECURITY
  633. SELECT
  634. SELF
  635. SENSITIVE
  636. SEPARATOR
  637. SEQUENCE
  638. SERIALIZABLE
  639. SERVER_NAME
  640. SESSION
  641. SESSION_USER
  642. SET
  643. SETOF
  644. SETS
  645. SETUSER
  646. SHARE
  647. SHOW
  648. SHUTDOWN
  649. SIGNAL
  650. SIMILAR
  651. SIMPLE
  652. SIZE
  653. SMALLINT
  654. SOME
  655. SONAME
  656. SOURCE
  657. SPACE
  658. SPATIAL
  659. SPECIFIC
  660. SPECIFIC_NAME
  661. SPECIFICTYPE
  662. SQL
  663. SQL_BIG_RESULT
  664. SQL_BIG_SELECTS
  665. SQL_BIG_TABLES
  666. SQL_CALC_FOUND_ROWS
  667. SQL_LOG_OFF
  668. SQL_LOG_UPDATE
  669. SQL_LOW_PRIORITY_UPDATES
  670. SQL_SELECT_LIMIT
  671. SQL_SMALL_RESULT
  672. SQL_WARNINGS
  673. SQLCA
  674. SQLCODE
  675. SQLERROR
  676. SQLEXCEPTION
  677. SQLSTATE
  678. SQLWARNING
  679. SQRT
  680. SSL
  681. STABLE
  682. START
  683. STARTING
  684. STATE
  685. STATEMENT
  686. STATIC
  687. STATISTICS
  688. STATUS
  689. STDDEV_POP
  690. STDDEV_SAMP
  691. STDIN
  692. STDOUT
  693. STORAGE
  694. STRAIGHT_JOIN
  695. STRICT
  696. STRING
  697. STRUCTURE
  698. STYLE
  699. SUBCLASS_ORIGIN
  700. SUBLIST
  701. SUBMULTISET
  702. SUBSTRING
  703. SUCCESSFUL
  704. SUM
  705. SUPERUSER
  706. SYMMETRIC
  707. SYNONYM
  708. SYSDATE
  709. SYSID
  710. SYSTEM
  711. SYSTEM_USER
  712. TABLE
  713. TABLE_NAME
  714. TABLES
  715. TABLESAMPLE
  716. TABLESPACE
  717. TEMP
  718. TEMPLATE
  719. TEMPORARY
  720. TERMINATE
  721. TERMINATED
  722. TEXT
  723. TEXTSIZE
  724. THAN
  725. THEN
  726. TIES
  727. TIME
  728. TIMESTAMP
  729. TIMEZONE_HOUR
  730. TIMEZONE_MINUTE
  731. TINYBLOB
  732. TINYINT
  733. TINYTEXT
  734. TO
  735. TOAST
  736. TOP
  737. TOP_LEVEL_COUNT
  738. TRAILING
  739. TRAN
  740. TRANSACTION
  741. TRANSACTION_ACTIVE
  742. TRANSACTIONS_COMMITTED
  743. TRANSACTIONS_ROLLED_BACK
  744. TRANSFORM
  745. TRANSFORMS
  746. TRANSLATE
  747. TRANSLATION
  748. TREAT
  749. TRIGGER
  750. TRIGGER_CATALOG
  751. TRIGGER_NAME
  752. TRIGGER_SCHEMA
  753. TRIM
  754. TRUE
  755. TRUNCATE
  756. TRUSTED
  757. TSEQUAL
  758. TYPE
  759. UESCAPE
  760. UID
  761. UNBOUNDED
  762. UNCOMMITTED
  763. UNDER
  764. UNDO
  765. UNENCRYPTED
  766. UNION
  767. UNIQUE
  768. UNKNOWN
  769. UNLISTEN
  770. UNLOCK
  771. UNNAMED
  772. UNNEST
  773. UNSIGNED
  774. UNTIL
  775. UPDATE
  776. UPDATETEXT
  777. UPPER
  778. USAGE
  779. USE
  780. USER
  781. USER_DEFINED_TYPE_CATALOG
  782. USER_DEFINED_TYPE_CODE
  783. USER_DEFINED_TYPE_NAME
  784. USER_DEFINED_TYPE_SCHEMA
  785. USING
  786. UTC_DATE
  787. UTC_TIME
  788. UTC_TIMESTAMP
  789. VACUUM
  790. VALID
  791. VALIDATE
  792. VALIDATOR
  793. VALUE
  794. VALUES
  795. VAR_POP
  796. VAR_SAMP
  797. VARBINARY
  798. VARCHAR
  799. VARCHAR2
  800. VARCHARACTER
  801. VARIABLE
  802. VARIABLES
  803. VARYING
  804. VERBOSE
  805. VIEW
  806. VOLATILE
  807. WAITFOR
  808. WHEN
  809. WHENEVER
  810. WHERE
  811. WHILE
  812. WIDTH_BUCKET
  813. WINDOW
  814. WITH
  815. WITHIN
  816. WITHOUT
  817. WORK
  818. WRITE
  819. WRITETEXT
  820. X509
  821. XOR
  822. YEAR
  823. YEAR_MONTH
  824. ZEROFILL
  825. ZONE

Symboles de substitution temporaires et délimiteurs

Référence sur drupal.org : 19 Avril 2009 – 00h48 - http://drupal.org/node/209715


Il est tenant d’utiliser un obscur symbole comme caractère de substitution, surtout s’il n’y a que votre code qui peut le voir : mais ce n’est pas garanti. Les symboles non imprimés, invalides ou non documentés peuvent ne pas être traitées correctement dans le cas improbable où ils seraient  vus par un navigateur ou un aggrégateur de news. Et moins ils sont susceptibles d’êtres vus, moins ils sont susceptibles d’être testés. Ce qui signifie qu’il faudra coder de quoi supprimer ces insidieux symboles, y compris dans le code que vous utilisez à cette fin.

Pour éviter cela, et pour allonger la durée de vie de votre code, utilisez des chaînes alphanumériques adéquates – préfixées du nom du module et un tiret – ou un signe souligné _ et encadré de crochets […].

Si vous avez besoin de délimiter des symboles de substitution, le délimiteur fermant peut inclure un / après le [ initial et peut suffixer le nom du module.

Trouver vos symboles de substitution

Une PCRE comme '@\[modulename-tag\](.+?)\[/tag-modulename\]@' peut être utilisée pour trouver la chaîne que vous avez précédemment délimité.

Utilisez les fonctions unicode Drupal pour les chaînes

Référence sur drupal.org : 26 Mai 2009 – 18h05 - http://drupal.org/node/473460


Si vous écrivez un module ou un thème, sachez qu’il pourra être utilisé sur des sites du monde entier, certains pouvant utiliser des langues dont les caractères sont codés sur plusieurs octets en Unicode plutôt que sur le seul octet des formats ASCII ou Européen. Certaines des fonctions intégrées de PHP pour le traitement des chaînes ne fonctionnent pas correctement sur des textes multi-octets.

Pour cette raison, Drupal fournit des fonctions qui remplacent les fonctions intégrées de PHP. Vous devez les utiliser lorsque vous programmez pour Drupal, sauf quand cela est précisé. Le module Coder peut vérifier votre module en vue de ces remplacements.

Voici ces fonctions :

Si vous programmez du texte, consultez également le guide to handling text in a secure fashion .

Ecrire du code conforme à E_ALL

Référence sur drupal.org : 9 Avril 2009 – 01h34 - http://drupal.org/node/34341


E_ALL : une meilleure pratique

Actuellement, le code Drupal n’est pas coforme E_STRICT. Quand vous faites tourner un site Drupal avec E_ALL, chaque page crée des tonnes de messages d’erreurs. Beaucoup de développeurs Drupal pensent qu’il serait bon que le code Drupal soit mis en conformité avec les bonnes pratiques couramment admises.

Le but de ce document est double :

  1. Montrer comment des erreurs courantes de programmation empêchent Drupal d’être conforme à E_STRICT
  2. Fournir de meilleures règles de programmation pour le code nouveau et les patches.

Une fois que ces règles seront acquises, ce ne sera plus qu’une question de temps, et d’efforts pour les développeurs, pour que tout le code antérieur soit corrigé. Nous pourrons ensuite faire tourner Drupal avec la directive E_ALL.

Erreurs de programmation courantes et nouvelles habitudes

Utilisation de if (isset($var)) ou if (!empty($var))

Si vous voulez vérifier qu’un tableau a bien reçu une valeur, ne faites pas :

<?php
if ($foo) {}
?>

mais faites plutôt :

<?php
// either
if (isset($foo)) {} // $foo=0 (zero) and $foo= '' return TRUE
// or
if (!empty($foo)) {} // use this when 0 or '' are not expected
// and are not valid values for $foo.
?>

La différence entre isset() et !empty() est qu’à l’inverse de !empty(), isset() renverra TRUE même si la variable a été initialisée avec une chaîne vide ou l’entier 0. Pour déterminer quelle fonction utiliser, étudiez si 0 ou '' sont des valeurs possibles pour votre variable.

Le code suivant est incorrect :

<?php
function _form_builder($form$parents = array(), $multiple FALSE) {
  
// (...)
  
if ($form['#input']) {
    
// some code (...)
  
}
}
?>

Ici, la variable $form est passée à la fonction. Si $form['#input'] a été initialisé avec n’importe quelle valeur, some code est exécuté. Le problème est qu’en testant de cette façon, cela affiche le message d’erreur suivant :

notice: Undefined index:  #input in includes/form.inc on line 194.

Même si le tableau $form est déjà déclaré et passé à la fonction, chaque index doit exlicitement être déclaré. Le code précédent devrait plutôt être écrit comme ceci :

<?php
function _form_builder($form$parents = array(), $multiple FALSE) {
  
// (...)
  
if (!empty($form['#input'])) {
    
// some code (...)
  
}
}
?>

Attention !

La fonction isset() renvoie TRUE quand la variable est initialisée avec l’entier 0, mais FALSE lorsqu’elle l’est avec la valeur NULL. Dans certains cas, is_null() est préférable, surtout lorsqu’il s’agit de tester une valeur retournée par une requête SQL.

Tester les messages d’erreurs

Si vous voulez aider au nettoyage du code Drupal pour qu’il soit confome à E_STRICT, vous pouvez paramétrer un site de test et modifier le fichier includes/common.inc :

<?php
if ($errno & (E_ALL E_NOTICE)) {
?>

en :

<?php
if ($errno & (E_ALL )) {
?>

Règles de documentation des modules

Référence sur drupal.org : 8 Mars 2009 – 03h59 - http://drupal.org/node/161085


Lorsque vous soumettez (ou maintenez) des modules, l’existence d’une bonne documentation est vitale pour ceux qui viennent après vous. De nombreux ouvrages en parlent, et tout développeur digne de ce nom a son idée sur ce qui constitue une bonne documentation. Il y a toujours une ligne de démarcation entre trop de documentation et pas assez.

Les indications qui suivent doivent être utilisées pour les modules qui feront partie des modules contributifs de Drupal.

Obligations de base

Ayez un README utile

Tous les modules, sauf les plus simples, doivent être accompagnés d’un README.txt. Ce fichier doit contenir un aperçu de ce que fait le module et de comment l’utiliser. Ce peut être une copie du résumé figurant sur la page du projet. Il est également conseillé de faire un lien direct vers le README.txt depuis la page du projet :

http://cvs.drupal.org/viewvc.py/drupal/contributions/modules/modulename/...

Ce fichier doit donner suffisamment d’informations à l’utilisateur pour qu’il puisse évaluer si le module correspond à ses besoins avant qu’il ne le télécharge et ne l’installe.

Utiliser hook-help

Tous les modules, sauf les plus simples, doivent, dans une certaine mesure, implémenter hook_help(). Reportez-vous à Embedded documentation style guide pour voir comment structurer votre documentation. Sinon, vous pouvez simplement inclure le README avec une fonction comme :

<?php
/**
* Implementation of hook_help().
*/
function mymodule_help($section) {
  switch (
$section) {
    case 
'admin/help#mymodule':
      
// Return a line-break version of the module README
      
return filter_filter('process'2NULLfile_get_contentsdirname(__FILE__)."/README.txt") );
  }
}
?>

Les pages d’aide Drupal sont évidemment l’endroit pour accéder aux informations sur votre module, à un niveau utilisateur ou développeur.

Dites aux utilisateurs ce que fait le module

Le README et l’aide doivent décrire comment accéder aux fonctionnalités du module.

Si le module fournit quelques éléments de menus, indiquez les chemins pour que l’utilisateur n’ait pas à les chercher dans l’interface d’administration.

S’il modifie des Interface Utilisateur de formulaires avec l’API Forms, dites à l’utilisateur ce qu’il doit chercher pour être sûr que ça marche.

Si le module a besoin d’un paramétrage, fournissez le lien vers la page d’administration et écrivez éventuellement un petit guide de démarrage.

Réfléchissez au nom du « package »

Vous pouvez définir un groupe dans le fichier module.info. Beaucoup de modules n’utilisent pas du tout de « package » groupe. Citation de la page « Comment écrire les fichiers .info » : « En règle générale, ce champ ne devrait être utilisé que par les grands  modules multi-package, ou par les modules qui étendent ces packages, comme CCK, Views, E-Commerce, Organic Groups et assimilés ». Si vous avez une utilisation valable du package name, utilisez la liste existante des groupes de projets pour intituler votre module, veillez au respect des majuscules/minuscules.

Documentez toutes les fonctions correctement

Dans vos modules, vous êtes invité à utiliser des commentaires formatés Doxygen . Bien que cela ressemble parois à une tarte à la crème, apprenez à les apprécier, puisque ce sont ces commentaires qui alimentent l’intégralité du site api.drupal.org et peuvent faire la même chose pour vous en utilisant le module API. Votre code peut être auto-documenté... gratuitement !

De plus, beaucoup d’IDE sont à même de détecter automatiquement les blocs de documentation Doxygen, et de fournir une aide contextuelle. Sans compter les avantages évidents pour les personnes qui lisent votre code.

Lisez les règles d’écriture de commentaires et notamment m’utilisation de la directive @ingroup.

Pratiques recommandées

Les suggestions qui suivent ne sont pas sacro-saintes mais peuvent aider à rendre votre module plus developpeur-friendly.

  • Même si votre module n’a pas besoin d’une fonction hook_install, il est sympa d’afficher un mot confirmant qu’il a été installé et prêt à l’emploi. La fonction hook_install se trouve dans le fichier module.install :
    <?php
    /**
    * Implementation of hook_install().
    */
    function mymodule_install() {
      
    drupal_set_message(st("YourModule settings are available under !link",
        array( 
    '!link' => l('Administer > Site configuration > Your Module ',  'admin/settings/yourmodule/settings' ) )
      ));
    }
    ?>

    Notez que dans les fichiers .install, vous devez utilise st() plutôt que t().
  • Créez des liens renvoyant aux pages d’utilisation et de paramétrage du module (ainsi qu’à sa page d’aide). Ces pages se trouvent souvent à plusieurs clics de souris les unes des autres ! Utiliser la méthode hook_help pour ajouter un paragraphe en haut des pages concernées peut aider. Des liens de ce type sont probablement plus utiles que toutes sortes de chapitres d’explications dans un document annexe.
  • Quand vous créez votre page de projet, activez le lien vers « Browse CVS Repository ».
  • Si cela est utile, n’hésitez pas à ajouter de la documentation supplémentaire comme partie intégrante du projet, dans un sous-dossier appelé docs/.
  • Les copies d’écrans de l’Interface Utilisateur ou des schémas des processus sont très utiles. Même si vous ne pouvez pas inclure des images dans la page de votre projet, vous pouvez créer des liens directs vers une documentation supplémentaire dans le CVS aux fins d’illustration.
  • Essayez d’installer et d’exécuter votre code via le module API. Ce n’est pas la panacée mais cela indiquera le niveau de votre documentation et ce qu’elle pourrait être si vous en modifiez un peu le formatage.
  • Si vous hébergez votre développement ou votre site Drupal de démo quelque part, pensez à rendre disponible votre documentation du code  et à créer des liens vers elle depuis votre page du projet.
  • Installez et exécutez le module Coder. Il est franchement tatillon mais il rendra votre code plus facile à maintenir pour les utilisateurs.
  • Utilisez des comentaires sur une ligne
    // Sur leur propre ligne, avant leur sujet, en utilisant correctement majusculeset ponctuation.

    pour expliquer les points les plus délicats de votre code. La documentation palcée ici l’est pour les mainteneurs et les développeurs qui liront ’on l’espère) le code-source aussi facilement que la prose qui l’entoure. Les commentaires d’éclaircissements, les TODO, les restrictions connues, les aides-mémoire, les excuses pour le code légèrement surabondant et autres avertissements se mettent ici.
  • Utilisez sans compter les références Doxygen @see pour lier les fonctions connexes.
  • En ayant des fonctions courtes (moins de 1 page) vous n’aurez pas rédiger beaucoup de détails d’implémentation.
  • Encouragez les contributeurs à améliorer la documentation. Un utilisateur peut parfaitement contribuer pour rien de plus que clarifier des commentaires et cette participation a sa place dans le CVS. Souvent, un regard extérieur verra les choses qui demandent un peu plus d’explications alors qu’elles vous sont familières.
  • Écrire un aperçu des buts du code et de sa méthodologie globale dans la section @file est souvent une idée très utile. Cela peut même aider à la conception du code durant la phase de planification.

Guide de balisage Drupal

Référence sur drupal.org : 9 Février 2009 – 11h54 - http://drupal.org/node/223584


Le Drupal Markup Style Guide est un document en cours de rédaction sur la façon de :

  • Créer un balisage HTML correct.
  • Créer des gabarits de base corrects pour être repris par des graphistes.
  • Créer des classes CSS correctes.

Normes de programmation JavaScript

Référence sur drupal.org : 26 Avril 2009 – 23h50 - http://drupal.org/node/172169


Indentation

Utilisez une indentation de 2 espaces, pas de tabulations. Pas d’espace après le dernier mot de la ligne.

Concaténation de chaînes

Utilisez toujours une espace entre le signe + et les chaînes à concaténer :

var string = 'Foo' + bar;
string = bar + 'foo';
string = bar() + 'foo';
string = 'foo' + 'bar';

Quand vous utilisez l’opérateur de concaténation +=, utilisez une espace de part et d’autre, comme pour le signe égal :

var string += 'Foo';
string += bar;
string += baz();

CamelCasing

A l’inverse des variables et fonctions déclarées dans le PHP Drupal, les noms de variables ou de fonctions JavaScript comportant plusieurs mots doivent être lowerCamelCased. La première lettre de chaque variable ou fonction doit être en minuscule, tandis que la première lettre de chaque mot doit être en capitale. Il ne devraient pas y avoir de signe souligné entre les mots.

Points-virgules

JavaScript autorise l’utilisation de toute expression comme instruction et utilise le point-virgule pour signaler la fin d’une instruction. 

Cependant, cela devient facultatif avec les « semi-colon insertion » 

(ajout, par JavaScript, d’un point-virgule entre deux instructions se trouvant sur deux lignes à la suite, exemple :

a = 1
b = 2

devient

a =1;
b= 2;
)

mais cela peut masquer des erreurs et causer un échec de l’aggrégation du code.

Toutes les instruction doivent être suivies d’un ; à l’exception des suivantes :

for, function, if, switch, try, while

Ces exceptions sont les fonctions déclarées comme

  Drupal.behaviors.tableSelect = function (context) {
    // Statements...
  };

et

  do {
    // Statements...
  } while (condition);

Elles doivent toujours être suivies d’un point-virgule.

De plus, le valeur de l’expression return doit débuter sur la même ligne que le mot-clé return, pour éviter l’ajout du point-virgule.

Si l’option de performance Drupal 6 « Optimiser les fichiers JavaScript » est activé et s’il manque des points-virgule, alors l’aggrégation JS échouera. Il est donc très important que les points-virgules soient utilisés.

Structures de contrôle

Cela concerne if, for, while, switch, etc. Voici un exemple de déclaration IF, puisque c’est une des plus complexe :

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

Les déclaration de structures de contrôle doivent avoir une espace entre le mot-clé et la parenthèse ouvrante, afin de les distinguer des appels de fonctions.

Vous êtes fortement encouragés à toujours utiliser les accolades, même dans les cas où elles seraient techniquement facultatives. Ceci accroît la lisibilité et réduit les risques d’erreurs de logique qui peuvent apparaître avec l’ajout de nouvelles lignes.

switch

Pour les déclarations switch :

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

  case 2:
    action2();
    break;

  default:
    defaultAction();
}

Try

Les instructions try doivent avoir la forme suivante :

  try {
    // Statements...
  }
  catch (variable) {
    // Error handling...
  }
  finally {
    // Statements...
  }

for in

L’instruction for in permet de parcourir toutes les propriétés d’un objet. Malheureusement, tous les membres qui auront été hérités via la chaîne prototype seront également inclus dans la boucle. 

Pour éviter cela, le corps de chaque instruction for in doit être encadrée par une instruction IF agissant comme filtre. Elle peut sélectionner un type ou une série de valeurs particuliers, ou elle peut exclure des fonctions, ou elle peut exclure des propriétés du prototype. Par exemple :

for (var variable in object) if (filter) {
  // Statements...
}

Vous pouvez utiliser la méthode hasOwnProperty pour différencier les vrais membres de l’objet :

for (var variable in object) if (object.hasOwnProperty(variable)) {
  // Statements...
}

Fonctions

Fonctions et méthodes

Les méthodes et fonctions doivent être nommées en utilisant lowerCamelCase :

Drupal.behaviors.tableDrag = function (context) {
  for (var base in Drupal.settings.tableDrag) {
    if (!$('#' + base + '.tabledrag-processed', context).size()) {
      $('#' + base).filter(':not(.tabledrag-processed)').each(addBehavior);
      $('#' + base).addClass('tabledrag-processed');
    }
  }
};

Appels de fonctions

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

foobar = foo(bar, baz, quux);

Comme affiché ci-dessus, il devrait y avoir une espace de part et d’autre du signe égal utilisé pour affecter la valeur de retour de la fonction à la variable. Dans le cas de blocs comportant plusieurs affectations, plus d’espaces peuvent être insérées afin d’accroître la lisbilité :

short        = foo(bar);
longVariable = foo(baz);

Si une fonction littérale est anonyme, il doit y avoir une espace entre le mot function et la parenthèse gauche. Si l’espace est omis, il se pourrait que l’on prenne le mot « function » comme nom de fonction.

div.onclick = function (e) {
  return false;
};

Déclaration de fonctions

function funStuff(field) {
  alert("Ce script JS affiche des messages popup rigolos.");
  return field;
}

Les arguments ayant des valeurs par défaut se trouvent en dernières positions de la liste d’arguments. Tentez toujours de retourner une valeur d’une fonction s’il en existe d’adéquate.

Prenez en compte la notion de fonction anonyme décrite ci-dessus.

Variables et tableaux

Toutes les variables doivent être déclarée avec le mot var avant d’être utilisées et ne doivent être déclarées qu’une seule fois. Cela facilite la lecture du programme et la détection de variables non déclarées qui peuvent devenir implicitement globales.

La portée des  variables ne devrait pas être globale; essayez de leur donner une portée locale en les déclarant à tout prix dans une fonction. Toutes les variables doivent être déclarées au début des fonctions.

Constantes et variables globales

lowerCamelCasing doit être utilisé pour les constantes pré-définies. A l’inverse des normes pour le PHP, vous devez utilisez true, false et null puisque les majuscules ne sont pas valides en JavaScript.

Les variables ajoutées via drupal_add_js() doivent aussi être lowerCamelCased, pour être en conformité avec les autres variables une fois qu'elles seront utilisées dans le JavaScript.

<?php
drupal_add_js
(array('myModule' => array('basePath' => base_path())), 'setting');
?>

Cette variable sera référencée :

Drupal.settings.myModule.basePath;

Tableaux

Les tableaux devraient être formatés avec une espace séparant chaque élément et opérateur d’affectation, si le tableau le permet :

someArray = ['hello', 'world'];

Notez que si la ligne dépasse 80 caractères (ce qui est souvent le cas pour la déclaration  d’un formulaire ou d’un menu) chaque élément devra être disposé dans sa propre ligne, et indenter d’un niveau :

Notez qu’il n’y a pas de virgule après le dernier élément du tableau. C’est une différence avec les normes PHP. En JavaScript, mettre une virgule après le dernier élément du tableau causera une exception.

Commentaires

La documentation au sein du code-source devrait respecter les normes de formatage Doxygen.

Les commentaires divers sont fortement encouragés. Une règle générale dit que si vous regardez une portion de code et vous exclamez « La vache ! Je ne veux pas essayer d’expliquer ça ! », alors vous devez commenter le code avant d’oublier comment il marche. Les commentaires peuvent être enlevés ultérieurement par les utilitaires JS, ils n’impactent donc pas la taille du fichier téléchargé.

Les commentaires divers devraient utiliser des phrases avec des majuscules et ponctuées. Les lettres ne doivent être en capitales que pour désigner les constantes, par exemple : TRUE. Les commentaires devraient être sur une ligne à part juste avant la ligne de code ou le bloc de code qu’ils désignent. Par exemple :

// Unselect all other checkboxes.

Si chaque ligne d’une liste a besoin d’un commentaire, ils peuvent être placés sur la même ligne et indentés de façon uniforme pour une meilleure lisibilité.

Les commentaires comme en C /* */ et en C++ // sont valables. L’utilisation de commentaires à la façon Perl/shell # sont déconseillés.

Blocs de commentaires en en-tête

Tout code-source de Drupal doit contenir le commentaire suivant placé en début de fichier :

// $Id$

Cette balise sera complétée par le CVS avec les données utiles :

// $Id: CODING_STANDARDS.html,v 1.14 2008/02/19 03:36:41 ax Exp $

Emplacement du code JavaScript

Autant que faire se peut, le code JavaScript ne devrait pas être liée au HTML car cela  accroît significativement le poids d’une page sans offrir de possibilité de réduction par la mise en cache ou la compression.

Instruction with

L’instruction with est destinée à fournir un moyen d’accès rapide aux membres d’objets profondément imbriqués. Par exemple, il est possible (mais déconseillé) d’utiliser l’accès ci-dessous pour foo.bar.foobar.abc etc :

with (foo.bar.foobar) {
  var abc = true;
  var xyz = true;
}

Cependant, en regardant ce code, il est impossible de savoir lequel d’entre abc et xyz sera modifié. foo.bar.abc sera-t-il modifié ? Ou les variables globales abc et xyz ?

Vous devriez plutôt utiliser la version longue :

foo.bar.foobar.abc = true;
foo.bar.foobar.xyz = true;

ou si vous voulez réellement utiliser un raccourci, utilisez cette méthode :

  var o = foo.bar.foobar;
  o.abc = true;
  o.xyz = true;

Opérateurs

Comparaisons logiques

Les opérateurs == et != effectuent une conversion de type implicite avant d’effectuer la comparaison. Ce qui est une mauvaise chose puisque ' \t\r\n' == 0 sera vrai. Cela peut occulter des erreurs de types. Pour les comparaisons avec l’une des valeurs suivantes, utilisez les opérateurs === ou !== , qui ne provoquent pas de contraintes de type : <?php0 '' undefined null false true?>

Opérateur virgule

L’utilisation de l’opérateur virgule, qui entraîne l’exécution des expressions qui se trouvent à sa gauche et à sa droite, dans le sens gauche-droite et renvoie la valeur de l’expression située à sa droite, doit être évité. Un exemple d’utilisation est :

var x = (y = 3, z = 9);

Cette instruction initialise x à 9. Cette opération est déroutante pour les utilisateurs qui ne sont pas familiarisés avec cette syntaxe et rend plus difficiles la lecture et la compréhension du code-source. En conséquence, évitez l’utilisation de l’opérateur virgule, sauf dans la partie contrôle d’une instruction FOR.

Ceci ne s’applique pas au séparateur virgule utilisé dans les objets, tableaux, etc.

Éviter le code inaccessible

Pour éviter le code inaccessible, une instruction return, break, continue ou throw doit être suivie d’une } ou d'un case ou d'un default.

Constructeurs

Les constructeurs sont des fonctions conçues pour être utilisées avec le préfixe new. Ce préfixe crée un nouvel objet basé sur le prototype d’une fonction et lie cet objet aux fonctions par l’opérateur this. JavaScript n’émet pas d’avertissements si un new requis est omis. Si vous négligez le new, aucun nouvel objet ne sera créé et this sera lié à l’objet global (pas glop). 

Les constructeurs doivent être nommés avec une initiale en majuscule et une fonction dont l’initiale du nom est en majuscule ne devrait pas être appelée à moins qu’elle n’ait le préfixe new.

Utilisez des expressions littérales

Utilisez des expressions littérales à la place de l’opérateur new :

  • à la place de new Array(), utilisez []
  • à la place de new Object(), utilisez {}
  • n’utilisez pas les « wrappers » new Number, new String, new Boolean.

Dans la plupart des cas, la forme « wrapper » sera la même que l’expression littérale. Ce n’est cependant pas toujours le cas, voir l’exemple suivant :

var literalNum = 0;
var objectNum = new Number(0);
if (literalNum) { } // false because 0 is a false value, will not be executed.
if (objectNum) { }  // true because objectNum exists as an object, will be executed.
if (objectNum.valueOf()) { } // false because the value of objectNum is 0.

eval est mauvais

eval() est mauvais. Il demande au navigateur de créer un nouvel environnement de scripting (comme la création d’une nouvelle page web), d’importer toutes les variables de l’environnement en cours, d’exécuter le script, de lancer le ramasse-miettes (garbage collector) et d’exporter la variable dans l’environnement d’origine. De plus, le code ne peut être mis en cache aux fins d’optimisation.

Il s’agit probablement de la fonction la plus puissante et la plus mal employée de JavaScript. Elle a aussi ses alias. Aussi, n’utilisez pas le constructeur Function et ne passez pas des chaînes à setTimeout() ou setInterval().

Se prémunir contre les XSS

Toute sortie vers le navigateur qui a été fournie par l’utilisateur doit être exécutée dans la fonction Drupal.checkPlain(). Elle est semblable à la fonction PHP de Drupal check_plain() et encode les caractères spéciaux en plain-text pour l’afficher en tant qu’HTML.

TypeOf

Lorque vous utilisez une vérification tpeof, n’utilisez pas de parenthèse pour le typeof. Ce qui suit respecte les normes de programmation :

if (typeof myVariable == 'string') {
  // ...
}

Modifier le DOM

Lors de l’ajout de nouveaux éléments au DOM, n’utilisez pas document.createElement(). Pour des questions de compatibilité entre les navigateurs et aussi dans le but de réduire la taille des fichiers, utilisez son équivalent jQquery.

Ne faites pas :

this.popup = document.createElement('div');
this.popup.id = 'autocomplete';

Mais faites :

this.popup = $('<div id="autocomplete"></div>')[0];

Des trucs propres à Drupal 6 (et ultérieurs)

Drupal 6 a introduit l’habillage JavaScript et la traduction des fichiers JavaScript.

Habillage (theming)

Il y a un mécanisme d’habillage pour le code JavaScript. Tout module contenant du JavaScript qui produit du contenu HTML doit fournir des fonctions de thèmes par défaut dans le Drupal.theme.prototype namespace.

Traduction des chaînes

Toutes les chaînes des fichiers JavaScript doivent être passées dans Drupal.t(), qui est l’équivalent de la célèbre fonction t(). De même, il y a aussi un équivalent à format_plural(), nommé Drupal.formatPlural(). L’ordre des paramètres est le même que pour les fonctions PHP.

Écrire du code sécurisé

Référence sur drupal.org : 15 Mai 2009 – 15h38 - http://drupal.org/writing-secure-code


Vous avez repéré un problème de sécurité ? Informez-en l’équipe de sécurité.

Que vous écriviez un petit bout de code ou un module de A à Z, il est important que votre code soit sécurisé.

Utilisez des fonctions de contrôle sur les sorties pour éviter les attaques par cross-scripting

Aucun contenu fourni par l’utilisateur ne doit être restitué tel quel dans le HTML.

Reportez-vous à Comment manipuler du texte de façon sûre pour plus d’informations

Utilisez la couche d’abstraction de base de données pour éviter les attaques par injection SQL

Utilisez la couche base de données correctement. Par exemple, ne concaténez jamais des données dans une requête SQL comme ceci :

<?php
db_query
('SELECT foo FROM {table} t WHERE t.name = '$_GET['user']); 
?>

A  la place, utilisez les caractères de substitution avec db_query :

<?php
db_query
("SELECT foo FROM {table} t WHERE t.name = '%s' "$_GET['user']); 
?>

S’il y a un nombre variable d’arguments dans votre requête, créer un tableau de caractères de substitution. Ne faites pas ceci :

<?php
db_query
("SELECT t.s FROM {table} t WHERE t.field IN (%s)"$from_user); 
?>

Mais faites cela :

<?php
$placeholders 
implode(','array_fill(0count($from_user), "%d"));

db_query("SELECT t.s FROM {table} t WHERE t.field IN ($placeholders)"$from_user); 
?>

Utilisez db_rewrite_sql pour respecter les restrictions d’accès aux nodes

Beaucoup d’instructions SQL qui se réfèrent aux nodes ou à la table {node} doivent êtres emballées dans un appel à la fonction db_rewrite_sql() :

<?php
$result 
db_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n"));
?>

Le mécanisme d’accès aux nodes de Drupal requiert ces appels. Sans eux, les visiteurs pourraient avoir accès à des nodes pour lesquels ils n’ont pas d’autorisation d’accès.

Accès à la base de données

Référence sur drupal.org : 18 Mai 2009 – 20h51 - http://drupal.org/node/101496


Drupal fournit plusieurs fonctions pour envoyer des requêtes à la base de données. La forme classique est db_query. Utilisez toujours ces fonctions pour éviter les attaques par injection SQL. Cependant, n’utiliser que ces fonctions n’est pas suffisant, comme le montre l’exemple suivant :

<?php
/** Exemple 1 - Risqué
  * SQL injection via $type
 * Affiche les titres de nodes du type $type (indiqué par l'utilisateur vie une zone de saisie de formulaire)
*/
$result db_query("SELECT n.nid, n.title FROM {node} n WHERE n.type = '$type'");

$items = array();
while (
$row db_fetch_object($result)) {
  
$items[] = l($row->title"node/{$row->nid}");
}
return 
theme('item_list'$items);
?>

Cet exemple affiche une liste de titres dépendants de l’argument fourni par l’utilisateur. Une liste de nodes page sera extraite lorsque $type sera page, une liste de nodes articles lorsque $type sera article. Malheureusement, cet exemple est vulnérable à une injection SQL.

La vulnérabilité peut être utilisée sur les bases de données supportant UNION (MySQL 4.1+) en obtenant un accès utilisateur au site, en indiquant comme type : story' UNION SELECT s.sid, s.sid FROM {sessions} s WHERE s.uid = 1/*.

Ce qui générera la requête suivante :

SELECT n.nid, n.title FROM {node} n WHERE n.type = 'story' UNION SELECT s.sid, s.sid FROM {sessions} s WHERE s.uid = 1/*'

Comme ce code affichera des ID de session valides pour le compte administrateur, un agresseur pourra indiquer à son navigateur d’utiliser ces ID et aura tous les droits sur le site.

Les requêtes paramétrées empêchent les injections SQL

Empêcher l’injection SQL est facile : db_query fournit une méthode d’utilisation des requêtes paramétrées. Les fonctions de bases de données de Drupal remplacent le caractère de substitution avec l’argument correctement échappé, dans leur ordre d’apparition.

<?php
db_query
("SELECT n.nid FROM {node} n WHERE n.nid > %d"$nid);
db_query("SELECT n.nid FROM {node} n WHERE n.type = '%s'"$type);
db_query("SELECT n.nid FROM {node} n WHERE n.nid > %d AND n.type = '%s'"$nid$type);
db_query("SELECT n.nid FROM {node} n WHERE n.type = '%s' AND n.nid > %d"$type$nid);
?>

Les caractères de substitution valides sont renseignés dans la documentation de l’API :

  • %d – entiers
  • %f - flottants
  • %s – chaînes, entourées de ’’
  • %b – données binaires, non entourées de ’’
  • %% - remplacé par %

Voici le premier exemple corrigé :

<?php
/** Exemple 1 - Corrigé
  * Affiche les titres de nodes du type $type (indiqué par l'utilisateur vie une zone de saisie de formulaire)
  */
$result db_query("SELECT n.nid, n.title FROM {node} n WHERE n.type = '%s'"$type);

$items = array();
while (
$row db_fetch_object($result)) {
  
$items[] = l($row->title"node/{$row->nid}");
}
return 
theme('item_list'$items);
?>

Arguments multiples

De temps à autre, vous aurez besoin d’injecter plusieurs arguments dans une requête, en utilisant la fonction IN().

Dans Drupal 5, vous utiliserez ce code :

<?php
$placeholders 
implode(','array_fill(0count($from_user), "%d"));

db_query("SELECT t.s FROM {table} t WHERE t.field IN ($placeholders)"$from_user); 
?>

A partir de Drupal 6, une fonction db_placeholders est disponible. Exemple :

<?php
$values 
= array(12345);

$placeholders db_placeholders($values);

db_query("SELECT t.s FROM {table} t WHERE t.field IN ($placeholders)"$values); 
?>

Manipuler les saisies Utilisateur avec précaution

Référence sur drupal.org : 2 Mars 2009 – 03h45 - http://drupal.org/node/101495


Les saisies, qu'elles viennent des internautes ou des serveurs, doivent être manipulées avec précaution.

Prenons l’exemple suivant, basé sur A programmer Introduction to PHP 4.0 de W.J. Gilmore. Il utilise une saisie effectuée par un utilisateur et la place dans une requête SQL :

<?php
/** Exemple 1 - Périlleux
  * SQL injection via $keyword
  */
$keyword $_REQUEST['keyword'];
$query "SELECT cust_id, cust_name, cust_email FROM customers WHERE category = '$keyword'";
$result db_query($query);
?>

Ceci permet à l’utilisateur d’injecter des instructions SQL dans une requête. Que se passera-t-il si l’utilisateur utilise le mot-clé '; DROP TABLE customers; -- ? La requête sera SELECT cust_id, cust_name, cust_email FROM customers WHERE category = ''; DROP TABLE customers; --'

La méthode Drupal : Échapper ou filtrer à bon escient

Vous avez peut-être déjà lu ce conseil d’expert dans les articles traitant des problèmes de sécurité : toujours valider les saisies. Le problème avec un CMS comme Drupal est que la notion de saisie n’est pas bien définie.

La seule chose que nous ayons à faire est que, indépendamment des données, leur contenu ne doit pouvoir être interprété comme du SQL. Nous utilisons pour cela les fonctions d’échappement fournies par l’API database. Nous faisons cet échappement dans la couche database et non juste après la saisie puisqu’il pourrait y avoir plus d’un échappement, ou filtrage, à faire.

<?php
/** Exemple 2 - Périlleux
  * SQL injection via $keyword
  * XSS via $keyword et (potentiellement) $row->cust_name, $row->cust_email
  */
$keyword $_REQUEST['keyword'];
$query "SELECT cust_id, cust_name, cust_email FROM customers WHERE category = '$keyword'";
$result db_query($query);
echo 
"<h2>$keyword</h2>":
while (
$row db_fetch_object($result)) {
  echo 
"$row->cust_name, $row->cust_email <br />";
}
?>

Exemple 2 : bien qu’il soit modeste et truffé de failles, il met en évidence le problème du filtrage et de l’échappement des saisies. La variable $keyword doit être filtrée ou échappée deux fois pour différentes raisons : une pour empêcher les injections SQL, et une de plus pour empêcher les attaques par cross-scripting (XSS). Appliquer les deux filtres dès la saisie de $keyword provoquera l’apparition d’étranges barres obliques (slashes) dans les affichages, ou l’échec des recherches contenant des &quot;

La solution consiste en utiliser le filtre adéquat au bon moment. Par exemple, juste avant d’envoyer le texte au navigateur  ou avant de le mixer avec du code HTML, échappez-le avec check_plain.

<?php
/** Exemple 2 - corrigé
  */
$keyword $_REQUEST['keyword'];
$query "SELECT cust_id, cust_name, cust_email FROM customers WHERE category = '%s'";
$result db_query($query$keyword);
echo 
'<h2>'check_plain($keyword) .'</h2>':
while (
$row db_fetch_object($result)) {
  
// Not very elegant, but making a point.
  
echo check_plain($row->cust_name) .','check_plain($row->cust_email) .'<br />';
}
?>

Créer des formulaires sécurisés pour éviter les attaques Cross-site request forgeries (CSRF)

Référence Drupal.org : 4 Février 2009 – 10h22 - http://drupal.org/node/178896


Une attaque Cross-site request forgery (CSRF ou XSRF ) consiste à effectuer une requête vers un site comme si elle venait d’un utilisateur, alors que l’utilisateur n’a pas demandé cette requête. Cela peut se faire de différentes façons, mais il est simple de se prémunir contre ces attaques avec Drupal.

Les spécifications HTTP 1.1 distinguent clairement les requêtes POST qui peuvent modifier des données dans le site (section 9.5) des requêtes GET qui ne doivent pas modifier de données (section 9.3). Les modules qui modifient des données devraient exiger une requête POST (par exemple, dans un formulaire).

Dans les versions de Drupal antérieures à l’annonce SA-2007-017 il était possible de créer des pages spécifiquement conçues qui contenaient des balises image dans  lesquelles l’élément src était un lien vers des menus désactivant les URL d’un site Drupal. Si l’administrateur du site visitait cette page alors qu’il était loggé dans son site, le navigateur effectuait une requête vers cette page, ce qui désavctivait alors les éléments de menu

In versions of Drupal prior to the release of SA-2007-017 it was possible to create a specifically formed page that contained image tags where the image "src" element was a link to certain menu "disable" URLs on a Drupal site. If a site admin visited that page when they were logged into their site then their browser would request the URL of the menu disable page which would then disable their menu items.

Se protéger contre CSRF dans Drupal

L’API Form fournit une protection contre les CSRF par l’ajout automatique de tokens spéciaux dans le formulaire. Si votre module utilise l’API Form pour toutes les requêtes qui modifient des données et si vous suivez correctement la documentation Form API, alors votre module est protégé contre les CSRF.

Code incorrect

Le code incorrect peut être de deux sortes :

  1. Par l’utilisation directe de variables $_POST et par la création de formulaires via HTML au lieu de passer par l’API Form de Drupal
  2. Par l’utilisation d’un lien et d’un menu appelant l’action qui modifie les données (notamment les actions destructrices comme la suppression de données)

Vous pouvez voir un exemple de mauvais code et sa correction dans Drupal Core CSRF vulnerabilities fixed in 5.2

Code correct

Voir la documentation de l’API Form.

Liens utiles

http://fr.wikipedia.org/wiki/Cross-Site_Request_Forgeries

Les fichiers : leurs téléchargements et leur gestion

Référence sur Drupal.org : 10 Avril 2008 – 12h02 - http://drupal.org/node/117054


(Cette section est en cours de rédaction)

Conseils brefs

Permettre la gestion des fichiers aux utilisateurs est potentiellement dangereux.

Vous devez être sûr qu’ils ne peuvent :

  • voir des fichiers divers
  • effacer des fichiers divers
  • écraser des fichiers sensibles
  • Uploader et exécuter des fichiers divers
  • saturer un disque ou votre quota de disque

Par « divers » on parle de n’importe quel fichier sur le serveur. Ainsi, par exemple, si vous restreignez l’accès au seul dossier files on ne parle plus de fichiers divers. Mais si le code pour l’écriture des fichiers permet à l’utilisateur d’altérer le chemin d’accès, alors il pourra ajouter un ../../ au nom de fichier, ce qui le placera en-dehors du dossier files/ et lui donnera accès à d’autres dossiers de votre serveur.

Voyez également File Permissions and Ownership For Security.

Dossiers

Référence sur drupal.org : 29 Janvier 2008 – 17h59 - http://drupal.org/node/117058


Pour les débutants : assurez-vous toujours que les actions sur les fichiers (upload, visualisation, donwload, suppression) se fassent dans le dossier files ou dans tout autre dossier réservé à cet effet. Faites attention au fait que dans les exemples ci-dessous, le dossier files est codé en dur dans le code, en réalité le nom de ce dossier est paramétrable.

Les utilisateurs n’ont pas à accéder aux fichiers système (tels que /etc/password ou sites/default/settings.php). Bien que les exemples ci-dessous s’intéressent surtout à la suppression des fichiers, ayez à l’esprit que lire des fichiers quels qu’ils soient est  néfaste.

<?php
/** Exemple 1 - Périlleux
  * Suppression arbitraire de fichier
  *
  * $file est le chemin/fichier (eg files/myfile.txt) fourni par l'utilisateur.
  */
file_delete($file);
?>

Dans ce premier exemple, un utilisateur malveillant peut outrepasser la confiance qui lui est donnée en fournissant des noms de fichiers dans différentes dossiers tels que /sites/default/settings.php. L’attaque est clairement limitée par les droits du compte utilisateur, qui exécute Drupal (souvent le serveur web), sur ces fichiers.

<?php
/** Exemple 2a - Périlleux
  * Suppression arbitraire de fichier
  *
  * $file est le nom de fichier (eg. myfile.txt) fourni par l'utilisateur.
  */
file_delete("files/$file");
?>
<?php
/** Exemple 2b - Périlleux
  * Suppression arbitraire de fichier
  *
  * $file est le chemin/fichier (eg files/myfile.txt) fourni par l'utilisateur.
  */
// Check whether $file is files/file
if (strpos($file"files/") === 0) {
  
file_delete($file);
}
?>

Les exemples 2a et 2b essaient d’atténuer l’attaque en s’assurant tout deux que le nom du fichier fourni soit précédé d’un nom de dossier. Ces deux exemples sont vulnérables à une attaque avec les dossiers parents (..).

Que se passerait-il si un utilisateur malveillant indiquait comme chemin :

Exemple 2a : ../sites/default/settings.php

Exemple 2b : files/../sites/default/settings.php

Les deux essaieront d’effacer sites/default/settings.php.

Pour une vérification correcte du vrai chemin d’un fichier, utilisez la fonction Drupal file_check_location.

<?php
/** Exemple 3
  * N'est plus vulnérable aux attaques de type chemin-parent (..) .
  *
  * $file est le chemin/fichier (eg files/myfile.txt) fourni par l'utilisateur.
  */
// Check whether $file is files/file
if (file_check_location($file'files') {
  
file_delete($file);
}
?>

Manipuler le texte de façon sûre

Référence sur drupal.org : 12 Novembre 2008 – 04h55 - http://drupal.org/node/28984


Lorsque vous manipulez et affichez du texte en HTML, vous devez veiller à ce qu’un filtrage ou un échappement correct soit effectué. Autrement, il pourrait y avoir des bugs si des utilisateurs utilisent des crochets ou des esperluettes. Ou pire, vous pourriez ouvrir la voie à des attaques XSS.

Lorsqu’on manipule des données, la règle est d’enregistrer exactement ce que l’utilisateur a saisi. Lorsqu’il modifie un texte créé précédemment, le formulaire doit  contenir exactement le texte saisi auparavant. Ce qui veut dire que les conversions se font lors de l’affichage du contenu, pas lors de sa sauvegarde dans la base de données (lisez bien la documentation db_query() sur la façon d’utiliser l’API database de façon sûre).

Pour déterminer les contrôles à effectuer, il peut être pratique de distinguer les textes selon leur nature. S’agit-il de texte seul (plain text), HTML, Bbcode ou Textile ? De là, chaque fois que vous concaténez deux chaînes de caractères, vous devez vous assurer qu’elles soient du même format. Si elles ne le sont pas, une vérification adéquate, une conversion ou un filtrage doit être appliqué.

Dans Drupal, les données soumises par les utilisateurs peuvent être réparties en trois catégories :

1. Texte seul (plain text)

C’est du texte simple, sans aucun balisage. Ce que l’utilisateur saisit est affiché tel quel à l’écran, sans aucune interprétation d’aucune sorte. C’est habituellement le format utilisé pour les champs texte sur une seule ligne.

Lorsque vous affichez du plain-text, vous devez le passer par check_plain() avant de l’inclure dans du HTML. Cela convertit les guillemets, les esperluettes et les crochets en entités HTML, ce qui fait que la chaîne est affichée littéralement dans le navigateur.

Beaucoup d’API et de fonctions « thémables » prennent du HTML en argument et il y en a quelques-unes qui assainissent automatiquement le texte en le passant à check_plain() :

  • t() : les caractères de substitution (comme %name ou @name) sont passés en tant que texte pur (plain-text) et seront échappés lors de l’insertion dans une chaîne traduisible. Vous pouvez désactiver cet échappement en utilisant les caractères de substitution (placeholders) du formulaire !name! (plus d’infos)
  • l() : le texte du lien doit être passé en tant que plain-text (à moins qu’il ne soit surchargé avec le paramètre $html)
  • éléments de menu et fils d’Ariane (breadcrumb) : les titres des éléments de menu et le titre du fil d’Ariane sont automatiquement assainis.
  • theme(’placeholder’) : le caractère de substitution est du plain-text
  • Descriptions de blocs (mais pas les titres, voir plus bas)
  • Noms d’utilisateurs lorsqu’ils sont affichés avec theme_username()
  • Form API : les éléments #default_value et #options lorsque le type est une liste de sélection.

    Exemples :
  • <?php
    $form
    ['safe'] = array(
      
    '#type' => 'textfield',
      
    '#default_value' => $u_supplied,
    );


    $form['also_safe'] = array(
      
    '#type' => 'select',
      
    '#default_value' => 0,
      
    '#options' => node_get_types('names'),  // Could contain unsafe values but FAPI will pass through check_plain() before displaying to user.
    );
    ?>

Certains emplacements exigent que tout texte soit préalablement assaini :

  • Les titres de page obtenus via drupal_set_title().

    Le titre de la page est affiché en HTML, où il est logique d’utiliser des balises comme <em> pour des raisons de clarté. Cependant, lorsque le titre de la page est affiché dans la balise <HTML>, toutes les balises sont supprimées.

    Exemples :
    <?php
    drupal_set_title
    ($node->title); // XSS vulnerability, bad
    drupal_set_title(check_plain($node->title));  // Correct
    ?>
  • Les titres de blocs passés par hook_block(). Pour la même raison que pour le titre de page, l’utilisation d’HTML est ici courante.
  • Messages Watchdog.

    Exemples :
    <?php
    Drupal 5
    :
    watchdog('content't("Deleted !title", array('!title' => $node->title))); // XSS
    watchdog('content't("Deleted %title", array('%title' => $node->title))); // or @

    Drupal 6 (The message and variables are passed through t() by the watchdog function):
    watchdog('content'"Deleted !title", array('!title' => $node->title)); // XSS
    watchdog('content'"Deleted %title", array('%title' => $node->title)); // or @
    ?>
  • Éléments de formulaire #description et #title

    Exemples
    <?php
    $form
    ['bad'] = array(
    '#type' => 'textfield',
    '#default_value' => check_plain($u_supplied),  // bad: escaped twice
    '#description' => t("Old data: !data", array('!data' => $u_supplied)), // XSS
    );

    $form['good'] = array(
    '#type' => 'textfield',
    '#default_value' => $u_supplied,
    '#description' => t("Old data: @data", array('@data' => $u_supplied)),
    );
    ?>

  • Éléments de formulaire #options lorsque #type = chekcboxes ou #type = radios
    Exemples
    <?php
    $form
    ['bad'] = array(
      
    '#type' => 'checkboxes',
      
    '#options' => array($u_supplied0$u_supplied1),
    );

    $form['good'] = array(
      
    '#type' => 'checkboxes',
      
    '#options' => array(check_plain($u_supplied0), check_plain($u_supplied1)),
    );
    ?>
  • Éléments de formulaire #value de #type balisage et items doivent être sûrs. Notez que l’élément de formulaire #type est du balisage

    Exemples
    <?php
    $form
    ['unsafe'] = array('#value' => $user->name); //XSS
    $form['safe'] = array('#value' => check_plain($user->name));
    or
    $form['safe'] = array('#value' => theme('username'$user));
    ?>

2. Rich Text

Le Rich Text contient des balises HTML, Textile ou autres. Il est enregistré dans le format propre aux balises et converti en HTML lors de l’affichage grâce aux différents filtres activés. C’est le format généralement utilisé pour du texte multi-ligne.

Tout ce que vous avez à faire et de passer le rich text à check_markup() pour obtenir du HTML en retour, sûr pour l’affichage. Vous pouvez aussi permettre à l’utilisateur le choix du format de saisie, avec un widget de format via filter_form() et passer le format choisi à check_markup().

Vous devez vous assurer que l’auteur d’un message est autorisé à utiliser un format de saisie spécifique. Par sécurité, check_markup() effectué cette vérification pour l’utilisateur en cours par défaut. Toutefois, comme le contenu est filtré à l’affichage, ce n’est pas toujours la personnes à l’origine du contenu. Dans ce cas, vous devez désactiver cette vérification en passant $check = false à check_markup(), et en vous assurant que le format est contrôlé avec filter_access() lorsque le contenu est soumis.

3. Admin-only HTML

Depuis Drupal 4.7, il y a une troisième façon de traiter le texte. Il y a des parties de la section administration où l’on ne peut pas appeler le filtre système (pour le rich text) mais où des balises simples sont souhaitées, telles que des liens ou des italiques (donc le plain text n’est pas utilisable).

Ce peut-être le cas dans l’objectif du site, les règles d’utilisation et les descriptions de forums.

Pour ces cas-là, vous pouvez utiliser une zone de texte normale et passer le texte dans la fonction filter_xss_admin() pour l’affichage. Cela autorisera la plupart des balises HTML, tandis que cela bloquera les scripts ou les styles potentiellement nuisisbles.

Les URL dans Drupal doivent être manipulées de façon spéciale dans deux cas :

  1. Si des données dynamiques sont mises dans une URL, vous devez les passer dans urlencode(). Sinon, des caractères tels que # ou ? perturberont la sémantique de l’URL. urlencode() l’évitera en l’échappant avec la syntaxe %XX. Notez que les chemins Drupal (comme node/123) sont intégralement passés dans urlencode() depuis Drupal 4.7, il n’est donc pas nécessaire d’en urlencoder des parties. Ce confort ne s’applique pas aux autres parties de l’URL comme les arguments des requêtes GET ou les morceaux d’identificateurs.
  2. Quand vous utilisez une URL utilisateur en tant qu’hyperlien, vous devez utiliser check_url() plutôt que check_plain() uniquement. check_url() appelera check_plain() et effectuera également quelques contrôles XSS pour s’assurer de la sûreté de l’URL.

Notez que toutes les fonction Drupal qui retournent des URL (url(), request_uri(), etc) affichent du texte seul, qui n’a pas été échappé du tout. Pour les échapper avant un affichage HTML (ou XML), utilisez check_url(). Mais ne l’utilisez pas dans les cas où une véritable URL est attendue, comme dans le header Location : ….

En pratique

Toutes les règles ci-dessus peuvent se résumer à ceci : rien de ce qu’un utilisateur soumet comme contenu ne doit être restitué tel quel dans le code HTML. Si vous n’êtes pas sûr, vous pouvez le tester en soumettant un bout de texte comme <u>xss</u> dans les champs de votre module. Si le texte est restitué souligné ou mutile les balises existantes, vous savez que vous avez un problème.

Voici des exemples de code corrects et incorrects. $title, $body et $url sont censés être des saisies utilisateurs contenant respectivement un titre, un bout de texte balisé et une URL. Elles sont issues de la base de données  et contiennent ainsi exactement ce que l’utilisateur a soumis, sans modifications.

Incorrect :

<?php print '<tr><td>$title</td><td>'?> <?php print '<a href="/..." title="$title">view node</a>';?>

Correct (le titre est du texte seul et ne doit pas être placé tel quel dans le code HTML):

<?php print '<tr><td>'check_plain($title) .'</td></tr>'?> <?php print '<a href="/..." title="'check_plain($title) .'">view node</a>'?>

Incorrect :

<?php print l(check_plain($title), 'node/'$nid);  ?>

Correct (l() appelle déjà check_plain() par défaut ):

<?php print l($title'node/'$nid); ?>

Incorrect :

<?php print '<a href="/$url">';  ?> <?php print '<a href="/'check_plain($url) .'">';  ?>

Correct (les URL doivent être vérifiées avec check_url()):

<?php print '<a href="/'check_url($url) .'">';  ?>

Écriture de filtres

Lorsque vous écrivez des filtres qui convertissent un langage de balises en code HTML, vous devez veillez à ne pas créer vous-même des failles de sécurité. D’une manière générale on applique les mêmes règles : vérifiez les URL avec check_url() et veillez à ce qu’aucun code HTML littéral puisse être injecté en l’échappant correctement avec check_plain().

JavaScript

Référence sur Drupal.org : 15 Février 2007 – 12h20 - http://drupal.org/node/119326


(Cette partie est en cours d’écriture sur Drupal.org)

Quelques règles générales :

  • Ne vous reposez pas sur le JavaScript pour effectuer des validations : il peut être désactivé par les utilisateurs.
  • Ne supposez pas que les données envoyées via AJAX proviennent de vos fonctions JavaScript.
  • Ne croyez pas que les données envoyées à une fonction, ou par une fonction JavaScript, ne peuvent pas être vues par l’utilisateur.
  • Faites attention, certaines fonction DOM décodent les entités HTML. Ne les réinsérez pas dans une page sans les avoir échappé.

ID de sessions

Référence sur drupal.org : 25 Janvier 2007 – 10:58 - http://drupal.org/node/101499


Les sessions PHP permettent de conserver des données durant la visite d’un utilisateur. Lorsqu’un internaute accède à votre site, un identifiant unique lui est assigné, l’ID de session. Côté internaute, cet ID est stocké dans un cookie et est envoyé à votre site à chaque requête de page.

Drupal enregistre les ID Utilisateurs dans la base de données. A chaque accès à une page, Drupal reçoit l’ID de session envoyé par la navigateur de l’internaute. Il cherche alors l’ID dans la table session pour trouver l’internaute associé. Cet ID Utilisateur sert à établir les droits que l’internaute a sur le site.

Pour conserver la sécurité de ce système, il est impératif de garder l’ID de session confidentiel. Si vous écrivez une module, vous ne devrez jamais rendre ces ID publics. Ils pourraient être lus par des utilisateurs et permettre le détournement des sessions.

Faites attention au fait que, même si votre sortie n’est pas affichée, si elle fait partie d’une requête AJAX par exemple, elle peut néanmoins être vue si l’utilisateur utilise un sniffer comme WireShark ou Fiddler.

Quand utiliser db_rewrite_sql

Référence sur Drupal.org : 15 Mai 2009 – 15h51 - http://drupal.org/node/93737


db_rewrite_sql() fournit aux modules une méthode pour compléter vos requêtes SQL. Par exemple, un module qui contrôle l’accès aux nodes devra restreindre le resultat de vos requêtes, en enlevant tous les nodes pour lesquels l’utilisateur n’a pas les droits d’accès.

Si vous n’utilisez pas db_rewrite_sql(), les contrôles d’accès des modules ne pourront pas modifier ou compléter vos requêtes SQL, et vous pourriez révéler sans le vouloir du contenu confidentiel.

C’est une bonne habitude de toujours utiliser db_rewrite_sql()

Quelques exceptions à cette règle :

  • Requêtes qui effectuent la cuisine interne d’un module, mais qui ne s’occupent pas d’afficher le contenu aux utilisateurs (par exemple : requêtes dans une tâche cron).
  • Requêtes des pages d’administration où il est nécessaire d’afficher des listes non filtrées et où l’utilisateur a toujours tous les privilèges.

Pourquoi Drupal filtre-t-il au moment de l’affichage ?

Référence sur drupal.org : 23 Février 2009 – 03h46 - http://drupal.org/node/263002


Une approche répandue dans d’autres applications web est de traiter ou filtrer les saisies utilisateurs pour des raisons de sécurité. Historiquement, Drupal préservait les saisies telles quelles et ne les filtraient qu’en sortie. Le sujet est débattu de temps à autre dans la communauté Drupal.

L’excellent article Safe string theory for the web de Steven Witten apporte une explication technique sur les raisons de filtrer selon les différents contextes des sorties.

Désormais, nous voyons que filtrer la saisie est problématique car nous ne pouvons connaître les caractères interdits sans connaître le contexte dans lequel ils apparaîtront.

Pour compliquer les choses, une chaîne donnée peut apparaître dans plus d’un contexte, par exemple en tant que texte HTML et aussi en tant qu’attribut comme dans <a title="$node->title">$node->title</a>. Donc si vous voulez filtrer tous les caractères, votre système sera paralysé parce que vous aurez besoin de filtrer trop de caractères, si vous voulez encoder, vous ne pouvez savoir comment encoder. L’encodage présente une autre problème, traiter du texte échappé est très malaisé (essayez d’extraire le teaser du corps d’un node HTML échappé).

Il n’y a pas d’autre alternative que d’enregistrer les saisies utilisateur sans modifications et d’appliquer les filtrages et les traitements appropriés lors des sorties.

Se faire passer pour un autre utilisateur sans dommages

Référence sur Drupal.org : 2 Juin 2009 – 03h47 - http://drupal.org/node/218104


Il y a une discussion pour déplacer cette fonctionnalité dans le core, pour que les développeurs puissent le faire sans risques : #287292: Add function to switch local user

Possibilités d’usurpation d’identité

Il y a plusieurs cas pour lesquels vous voudrez que votre code « usurpe » l’identité d’un autre utilisateur. C’est le cas lorsqu’une action d’utilisateur déclenche un autre processus. Si c’est autre processus doit être fait par un utilisateur différent, alors vous voudrez « usurper » l’identité de cet autre utilisateur.

Voici un exemple de code incorrect et risqué qui « usurpe » l’identité d’un autre utilisateur :

<?php
global $user;
$original_user $user;

$user user_load(array('uid' => 1));

// Take your action here where you pretend to be the user with UID = 1 (typically the admin user on a site)
// NOTE: - this is the unsafe part - if your code here fails, then the user suddenly has the permissions of UID 1!
$user $original_user;
?>

Le mode opératoire correct et sûr consiste à utiliser la fonction session_save_session() comme ci-dessous :

<?php
global $user;
$original_user $user;
session_save_session(FALSE);
$user user_load(array('uid' => 1));

// Take your action here where you pretend to be the user with UID = 1 (typically the admin user on a site)
// If your code fails, it's not a problem because the session will not be saved
$user $original_user;
session_save_session(TRUE);

// From here on the $user is back to normal so it's OK for the session to be saved
?>

« Sécurité : pourquoi s’embêter ? » Présentation de la Conférence Drupal 2008 à Barcelone

Référence sur Drupal.org : 19 Février 2008 – 03h55 - http://drupal.org/node/218037


Chaque fois qu’elle en à l’occasion, l’équipe Drupal Sécurité essaie de partager les informations sur ses méthodes et sur la façon d’écrire du code sécurisé dans Drupal . La présentation en pièce jointe a été réalisée lors de la DrupalCon de Barcelone en 2008.

Cette présentation traite de la sécurité à partir de « Why bother » puis examine comment les failles de sécurité sont souvent présentes, de quels types de faille il s’agit, lesquelles sont les plus courantes et comment résoudre la plupart des problèmes par l’utilisation correcte des API Drupal.

Fichier PDF de la présentation.

Documentation

Référence en anglais : 28 Avril 2009 - 20h17 - http://drupal.org/node/447604


Important : cet article est en cours de rédaction. Une discussion a lieu sur http://groups.drupal.org/node/14523.

La seule norme actuelle concernant le code contribué est de placer un fichier README.txt dans le paquetage (package). La documentation supplémentaire peut être placée dans un fichier INSTALL.txt.

Quelques README.txt sympas sont disponibles ici:

Meilleures pratiques de programmation

Référence en anglais : 17 Janvier 2009 - 00h33 - http://drupal.org/node/287350


Cette page expose brièvement nos meilleures pratiques pour programmer avec Drupal. Cela concerne autant des questions de haut niveau que celles abordées dans les pages Normes de programmation et Configuration & Usage Best Practices.

Le but de ces pages n'est pas de vous apprendre à programmer mais de de vous apprendre à devenir un meilleur programmeur dans le framework Drupal.

Tout le monde peut écrire du code, mais peu savent qu'il y a une façon de faire avec Drupal. Pourquoi la vitesse et les performances sont-elles si importantes ? Pourquoi documenter le code-source ? Pourquoi ce bout de code si utilisé est-il mauvais ? Nous répondons ici à ces questions, et à plein d'autres encore, et nous expliquons pourquoi.

Plan proposé

Connaître son API; ne pas réinventer la roue

Référence en anglais : 10 octobre 2008 - 17h17 -


Connaître l'API de Drupal n'est pas une tâche titanesque. La consulter chaque fois que vous pensez en avoir besoin est fortement conseillé. N'essayez pas de résoudre à tout prix les différentes questions de programmation par du codage brut. Au lieu de cela, chaque fois que vous pensez écrire plus de fonctions qu'il n'y en a besoin, arrêtez-vous et consultez la documentation de l'API pour voir s'il n'y a pas une façon de contourner votre problème. Si vous travaillez avec des modules contributifs, parcourir l'index des modules contributifs peut aider.

Admettez-le. Vous êtes intelligent mais il y a de bonnes chances que quelqu'un d'autre, quelque part, ait déjà essayé de faire ce que vous faites; et l'ait écrit dans une API. Vous pouvez avoir besoin d'une simple fonction, ou d'une série de fonctions, mais puisque l'un des buts d'une bonne programmation est la robustesse d'un code, utilisez l'API chaque fois que vous en aurez l'occasion.

Nommez les fonctions, nommez les variables

Référence en anglais sur drupal.org :27 Mai 2009 - 02h13 - http://drupal.org/node/299070


Nommez vos variables correctement

Choisir un nom adéquat pour vos fonctions est très important. Mais vous devriez apporter le même soin aux noms des variables. N'utilisez pas simplement $i et $j à tout bout de champ. Le nom de la variable doit clairement faire allusion à ce qu'elle contient. Si possible, regardez dans le core pour avoir un exemple des conventions de nommage des variables et utilisez les mêmes conventions dans votre code-source.

Faites également attention aux mots réservés, leur utilisation à mauvais escient vous mènera à des problèmes insoupçonnables et impossibles à débogguer.

Choix grammaticaux

Faites attention aux singuliers et aux pluriels pour le nom des scalaires et des tableaux, vous pouvez éviter des erreurs de programmation en adoptant une « écriture grammaticale » cohérente.

Class vs Variable globale

Vous pouvez aussi utiliser une classe plutôt que des variables globales. Vous pouvez ainsi nommer les variables sans avoir besoin de les préfixer avec le nom du module puisqu'il est contenu dans l'objet. Utilisez les fonctions magiques _set, _get et _toString pour contrôler les paramètres des variables via un tableau privé.

Plus c'est petit, mieux c'est

Référence en anglais sur drupal.org : 9 Janvier 2009 - 10h00 - http://drupal.org/node/299074


Si l'implémentation d'une fonction ne tient pas sur un écran de votre éditeur, c'est probablement qu'elle essaie de faire trop de choses. Elle devrait être divisée en fonctions plus petites.

  • Si elle tient sur un écran, vous pouvez être sûr que chacun comprendra ce qu'elle fait (et pourra la débogguer si quelque chose ne va pas).
  • Des petites fonctions qui ne font qu'une chose sont plus faciles à tester.
  • Des petites fonctions qui ne font qu'une chose sont plus facilement réutilisables.

Les traductions sont impératives

Référence en anglais sur drupal.org : 23 Août 2008 - 17h24 - http://drupal.org/node/299085


Accessibilité des chaînes de caractères

Drupal est disponible dans tous les pays du monde, et bien qu'il soit programmé en anglais, les chaînes de texte qu'il affiche sont traduites dans presque toutes les langues que vous pouvez envisager. La fonction t() est obligatoire pour toutes les chaînes de caractères qui seront lues par un utilisateur ou un administrateur, et c'est la seule façon qu'a le core pour traduire les chaînes en quelque chose de lisible par les lecteurs.

Sécurité des chaînes

t() est non seulement indispensable pour la localisation, mais dispose de fonctionnalités de sécurité importantes que chaque développeur Drupal devrait connaître et comprendre. Il est plus simple d'intégrer les textes dès le départ dans une fonction t() que de revenir dans le code ultérieurement pour le faire si vous (ou quelqu'un d'autre) décidez d'utiliser une autre langue.

Utilisez le contrôle de versions

Référence en anglais sur drupal.org : 23 Août 2008 - 16h57 - http://drupal.org/node/299067


Quand vous écrivez du code-source, placez-le dès que possible dans un système de contrôle de version et committez-le au fur et à mesure, avec des messages explicites sur ce que vous changez et pourquoi. N'attendez pas d'avoir votre release 1.0 pour tout committer d'un coup - le but d'un contrôle de version est de maintenir un historique de vos changements, de vos décisions et de leurs raisons.

Faîtes-en usage dès le début de votre projet. Ainsi, lorsque vous aurez besoin, des mois plus tard, de débogguer, modifier ou améliorer quelque chose, vous serez content de pouvoir comprendre ce que vous aviez fait initialement et les raisons pour lesquelles vous l'aviez fait.

Voir la conception initiale de votre code-source peut également aider d'autres personnes, qui voudront l'étendre, l'améliorer ou le débogguer.

Enfin, cela vous servira également de sauvegarde hors-site pour votre travail. Votre portable peut être volé ou exploser en plein vol, vos supers-idées seront toujours à l'abri dans le dépôt. N'oubliez pas que si un code-source n'existe pas en différents endroits, il n'existe pas du tout.

Écrire du code efficace

Référence du document en anglais sur drupal.org : 4 Mai 2009 - 08h19 - http://drupal.org/node/328206 - Incomplet


Dans le monde des programmeurs il y a une course. Une course pour savoir qui écrira le code qui s'exécutera le plus vite possible. Pas seulement le code le plus rapide, mais le code le plus utile. Et si vous êtes programmeur, qui est votre adversaire ? Vous bien sûr !

En tant que programmeurs, nous cherchons non seulement à écrire un code qui fasse quelque chose, mais aussi à ce qu'il le fasse bien. Nous parlons de ce «bien» en tant qu'efficacité. L'efficacité s'entend comme vitesse, économie de moyens et objectifs. Les trois doivent évoluer en harmonie. Et dans le cas de Drupal, la vitesse est très importante car nous ne voulons pas faire attendre l'utilisateur ou déclencher un timeout du serveur. Les objectifs sont également importants car Drupal n'est rien s'il ne peut rien proposer d'utile à son public.

Il y a une façon à considérer: celle d'un produit concret. Supposons que vous possédez une entreprise qui fabrique un produit B. Votre entreprise, comme toute entreprise qui fabrique des produits, est remise en question : produisez-vous beaucoup de produits B vraiment rapidement, ou votre produit B est-il très utile.

Dans un monde sans frontières nous devrons faire le maximum possible. Malheureusement, la fabrication du produit B ne tient pas compte des deux idées en même temps, d'où une limite. L'entreprise devra alors dépenser du temps et des efforts pour trouver le point d'équilibre. Un point d'équilibre dont nous connaissons les éléments : prix des éléments, travail, valeur et autres facteurs. Ainsi, une fois ces questions réglées, l'entreprise adapte ses méthodes de fabrication pour produire le plus de produits B tout en améliorant sa qualité.

Le parallèle avec le monde de la programmation est là (Ah. Je me disais aussi. NdT). Vous programmez pour atteindre un objectif (l'objectif est le produit B). Vous pouvez aussi programmer pour apporter une incroyable vitesse et d'aussi incroyables objectifs (et nous supposons que la qualité fait partie des objectifs).

Cependant, étant donné les limites des technologies modernes, vous ne pouvez obtenir les deux à la fois. Vous devez donc trouver votre point d'équilibre car vous ne pouvez obtenir les deux : la vitesse seule n'est rien s'il n'y a pas quelque chose à proposer, et proposer quelque chose ne doit pas prendre des plombes. Ceci - trouver le point d'équilibre - est ce que les pages suivantes de ce chapitre traiteront.

Les sujets présentés ici se rapportent à des problèmes simples à comprendre, qui seront également décrits. Ces sujets et problèmes incluent, entre autres, l'efficacité de la recursivité, le travail avec un ensemble de données sans rapport. En programmation, les problèmes peuvent facilement se résumer en une simple question, pourtant la plus simple des questions peut parfois demander des tonnes de réflexion pour être résolue.

Si vous avez un sujet relative à l'efficacité que vous voudriez voir ici (sur drupal.org ! NdT) n'hésitez pas à publier un commentaire sur cette page, d'aller en parler sur IRC, ou même d'ajouter cette page vous-même, si vous vous sentez au point sur le sujet.

(Ah oui, ça valait le coup de traduire ça. Vivement la suite. NdT)