Agile-compatible standard format for commit messages

When commiting changesets, I now write my messages using a special format that seems to me “Agile-compatible”. Each line of the message looks like the following: COD Description, where Description is a technical or (as in XOR) client-oriented description of the changeset (the user story summary, for example) and COD is one of:

  • NEW: a new feature or a new element of source code,
  • IMP: an improvement of one of the features of the projet or of the architecture/source code,
  • FIX: a fix of a bug,
  • REM: a removal of a feature or an element of the project (library, useless files…) -this is rarely used.

The Agile compatibility comes from the fact that if every commit must have a message and every message must follow the format, the commiter/developer can only do changes that can be labeled as a new feature, a fix, a feature removal or a (real) improvement. As an example, rewriting a function can only be a part of a changeset if it improves the project in some way: better performances, better readibility.

Some real world examples:

  • the creation of a project is a NEW Base libraries and build scripts,
  • the first feature is a NEW Authentication of a visitor of the website,
  • a fix of a feature is a FIX No error message when submitting an empty username in the authentication form,
  • a change of a feature related to a need is a IMP Moved the username and password fields on one line,
  • writing the documentation of a function is a IMP Documented the password encryption function,
  • etc.

Finally, using a label per change allows the commiter to have a better view on how to separate the commit in different changesets. Instead of commiting:

FIX PHP error when using a quote in the password field of the authentication form
IMP Getting the user info from the database only once when authentication

The developer can separate the changeset in two different commits (using partial commits or the shelve function of its SCM).

File-localized global (configuration) variables in PHP

I recently developed some functions that are meant to manage global variables within a PHP script.

For example, let us assume you want your script to save some configuration variables from an execution to another.
You may want to do something like:
<?php
...
$config = get_my_script_saved_config_vars();
...
if (!empty($_POST['myVar']))
{
$config['myVar'] = $_POST['myVar'];
save_my_script_config_vars($config);
}
...
if (!empty($config['myVar']))
{
do_some_actions_using_config_var($config['myVar']);
}
...
?>

Often, when dealing with this sort global/persistent configuration variables, you have to use a database or a special file.

Now, simply include this code to your script:
<?php
/**
* Provides a simple way to manage local persistent variables.
*
* @author Enisseo <http://www.enisseo.net>
*/
class LocalConfig
{
/**
* Returns the current local configuration.
* @return mixed
*/
public static function load()
{
$file = LocalConfig::_findFile();
$config = array();
if (!empty($file))
{
$content = file_get_contents($file);
$result = array();
if (preg_match('#<\?php\s+/\*CONFIG:(.*?)ENDCONFIG\*/ \?>#i', $content, $result))
{
$configStr = trim($result[1]);
$config = @unserialize($configStr);
}
}
return $config;
}
/**
* Save the current local configuration.
* @param $config mixed
*/
public static function save($config)
{
$file = LocalConfig::_findFile();
if (!empty($file))
{
$content = file_get_contents($file);
$configStr = '<?php /*CONFIG: ' . @serialize($config) . ' ENDCONFIG*/ ?>';
$content = preg_replace('#(<\?php\s+/\*CONFIG:(.*?)ENDCONFIG\*/\s+\?>|^)#i', $configStr, $content);
file_put_contents($file, $content);
}
}
/**
* Returns the caller file.
* @return string
*/
private static function _findFile()
{
$trace = debug_backtrace();
for ($i = 0; $i < count($trace); $i++)
{
if ($trace[$i]['file'] != __FILE__)
{
return $trace[$i]['file'];
}
}
return null;
}
}

You can either copy and paste into your script or include a file containing this script. Then, you only have to use the two static functions:
<?php
include_once('LocalConfig.php');
$config = LocalConfig::load();
if (!empty($config['name']))
{
print_form_asking_for_name();
}
if (!empty($_POST['name']))
{
$config['name'] = $_POST['name'];
LocalConfig::save($config);
}

This way you do not have to worry about databases or config files in order to store and manage persistent variables: the variables are stored within the file, in the begining of the source code.

If you find any bug, please let me know.

HTML Orienté Objet avec CSS

Ou Comment avoir (presque) l’impression de manipuler de l’objet en CSS.

HTML sémantique

Pour cela, il faut partir d’un code HTML sémantiquement fort. Les règles que j’utilise sont les suivantes :

  1. différencier les types de structures génériques des données spécifiques ;
  2. réfléchir à la forme HTML la plus appropriée pour chaque structure générique ;
  3. rester simple.

La première règle s’applique lorsqu’il n’existe pas de balises HTML existante permettant de décrire exactement le type de la structure. Un paragraphe est un <p>, une liste est un <ul>, mais un menu ? Un fil d’Ariane ? Une propriété d’un formulaire ?
Il faut de plus différencier ces structures génériques de leur réalité. Par exemple, une même page peut avoir plusieurs menus (même structure générique) mais l’un est le menu principal, l’autre le menu secondaire, etc. De la même façon, il existe plusieurs propriétés dans un formulaire mais chacune désigne une certaine donnée : identifiant, mot de passe, nom, prénom…

À chaque structure non existante, il est nécessaire de réfléchir à la structure la plus appropriée, à la fois sémantiquement et hiérarchiquement. Communément un menu est représenté par une liste d’éléments (<ul> et <li>). Un fil d’Ariane pourrait l’être par une liste ordonnée (<ol>). Une propriété d’un formulaire pourrait l’être par une liste de définitions (<dl>, <dt> pour le label, <dd> pour le champ). Il n’y a rarement qu’une seule bonne solution.
Lorsque les propriétés seules ne sont pas suffisantes, une pratique courante est d’augmenter le sens d’une balise existante grâce à un attribut HTML, notamment avec l’attribut class qui permet d’être différencié en CSS (voir les microformats). Ainsi, on pourra par exemple ajouter la classe menu sur le <ul> pour désigner un menu (<ul class="menu">), ou encore la classe “breadcrumb” sur un <ol> pour désigner un fil d’Ariane.

Enfin, il est essentiel de rester simple dans la construction du HTML : pas de profusion de <div>, de <p> et de <span> ! Pas besoin d’encapsuler un <h1> dans un <div class="title"> : le <h1> EST un titre.
Tant que c’est raisonnablement possible, fusionner les <div>, <p> et <span> avec les balises porteuses de sens et s’appuyer sur le contexte pour pouvoir distinguer les balises. Ainsi, on peut transformer <div class="ContactPage"><div class="ContactForm"><form>... en <div class="ContactPage"><form>..., voire en <form class="ContactPage>.

Après cette introduction, nous pouvons entrer dans le vif du sujet en commençant par utiliser de manière orientée objet les attributs HTML.

Classification et identification du HTML

Deux attributs permettent de s’approcher de la notion d’objets en HTML : class et id. Class peut être utilisé pour représenter la ou les classes (au sens POO du terme). De la même façon qu’un objet peut être de la classe Student qui hérite de User, un <div> a comme attribut class="Student User". L’attribut id sert quant à lui à identifier une instance d’un objet HTML : la <div id="student3188" class="Student"> correspond ainsi à une instance (appelée student3188) d’une classe Student.

Chaque partie du code HTML peut être décrite de cette manière. Par exemple, une liste d’utilisateurs peut être écrite sous la forme :

<ul class="Users">
   <li class="User" id="johndoe">
      <p class="Name">John Doe</p>
      <p class="Age"><span class="Value">25</span> years old</p>
   </li>
   <li class="User" id="janedoe">
      <p class="Name">Jane Doe</p>
      <p class="Age"><span class="Value">27</span> years old</p>
   </li>
</ul>

Une fois que le code HTML du site est organisé de cette manière, il est possible d’utiliser le CSS pour accéder aux différentes parties.

Écriture des sélecteurs CSS

L’écriture du code CSS s’appuie fortement sur la caractérisation des balises HTML expliquée précédemment. Grâce aux attributs class (sans balise HTML) il va être possible de mettre en forme de manière générique les données. L’attribut id servira quant à lui au besoin pour différencier les cas.
On peut donc écrire par exemple :

.Users {
   margin: 15px;
}
   .Users .User {
      border: 1px solid grey;
      padding: 10px;
   }
      .Users .User .Name {
         font-weight: bold;
      }
      .Users .User .Age {
         font-size: 0.8em;
      }
         .Users .User .Value {
            font-size: 1.5em;
         }
   .Users .User#janedoe {
      border-color: blue;
   }

Cette notation en classes donne l’impression d’écrire des accès à des variables en programmation orientée objet. En effet, les langages utilisent souvent le caractère . (point) pour accéder à la propriété d’un objet. Ainsi, on accède à l’attribut name de l’objet user par user.name. L’utilisation d’une organisation du code HTML et de sélecteurs CSS basés sur les classes permet d’approcher syntaxiquement cela : .User .Name.
Le fait de s’appuyer quasi uniquement sur l’arborescence de classes permet notamment :

  • d’éviter les conflits d’identifiant ;
  • de pouvoir accéder de manière lisible et compréhensible (sémantique) aux éléments ;
  • de réduire les soucis de priorité des sélecteurs CSS ;
  • de rester suffisamment souple et précis pour pouvoir tolérer des modifications du code HTML sans retouche du code CSS (encapsulation dans un autre bloc, modification du type de balise HTML…).

De cette manière les codes HTML et CSS approchent les qualités de modularité et de structuration de la programmation orientée objet.

Utiliser switch() pour gérer des processus

Voici une façon d’utiliser l’instruction switch présente sur de nombreux langages, notamment en PHP :

switch (false) {
   case doStep1(): print("Error at step 1"); break;
   case doStep2(): print("Error at step 2"); break;
   case doStep3(): print("Error at step 3"); break;
   default: print("Success!"); break;
}

Cette méthode permet notamment de gérer les retours en cas d’erreur lors du déroulement du processus. Les étapes vont être exécutées une à une et si l’une d’elles échoue (return false par exemple), le traitement associé à cette étape est exécuté puis le processus est terminé.
Si doStep1() retourne true, la comparaison avec le false du switch échoue et le programme tente avec le prochain case défini. Par contre, si doStep1() retourne false, la comparaison correspond (false == false) et le traitement associé au case est donc exécuté – en l’occurrence le traitement de l’erreur.

En somme, le fonctionnement n’est pas différent de :

if (doStep1()) {
   if (doStep2()) {
      if (!doStep3()) {
         print("Success!");
      } else {
         print("Error at step 3");
      }
   } else {
      print("Error at step 2");
   }
} else {
   print("Error at step 1");
}

ou encore de :

if (!doStep1()) {
   print("Error at step 1");
} else if (!doStep2()) {
   print("Error at step 2");
} else if (!doStep3()) {
   print("Error at step 3");
} else {
   print("Success!");
}

L’avantage de la notation en switch réside principalement dans sa lisibilité. En effet, chacune des notations précédente a au moins un défaut. Pour la première, il sont évidents : la cascade de conditions (indentation) et la séparation symétrique entre la condition et le traitement d’erreur (le traitement de l’étape 1 est à la fin, le message de succès est au milieu) entraînent une très mauvaise lisibilité du processus. Pour la deuxième, le processus reste lisible mais les conditions peuvent l’être moins puisqu’elles testent la non-validité de l’étape. Ainsi la compréhension des étapes est polluée par la négation de la condition : par le signe ! en préfixe dans l’exemple, mais éventuellement par des conditions plus tordues (!a || !b || !c…).
La structure en switch, pour peu qu’on la connaisse, offre une lisibilité adaptée au déroulement d’un processus : chaque étape se situe sur la même colonne, chaque traitement d’erreur ou de succès est proche de sa cause et les étapes sont lisibles. En faisant abstraction du langage en lui-même, la structure correspond à :

PROCESS:
   STEP1: ERROR1
   STEP2: ERROR2
   STEP3: ERROR3
   END: SUCCESS

Cette structure favorise également la décomposition de chaque étape en une fonction claire. Ainsi, au lieu d’avoir une accumulation de traitements mélangés comme c’est le cas lors de l’utilisation d’un if, la structure incite (en tout cas encourage) à nommer clairement chaque étape et à réaliser la fonction associée :

switch (false) {
   case enterLogin(): ... break;
   case enterPassword(): ... break;
   case validateForm(): ... break;
   case checkAuthentication(): ... break;
   ...
}

Lorsque quelqu’un relit le code, il peut très simplement comprendre le processus en lisant le nom de chaque fonction.

Enfin, le switch offre un léger avantage sur le if : l’instruction break. Cette dernière permet d’interrompre le processus à tout moment et peut éviter des constructions complexes. On peut par exemple écrire :

switch (false) {
   case doStep1():
      if (verbose == 0) break;
      print('Error');
      if (verbose == 1) break;
      print(error_msg());
      break;
   ...
   default:
      if (...) {
         break;
      }
      ...
}

Ce fonctionnement est moins naturel à réaliser avec un if.

Pour terminer, voici un exemple en syntaxe PHP d’une structure switch (false) qui mélange vérification et processus :

switch (false) {
   // checking if the process can be executed
   case doesTheUserWantToExecuteThisProcess():
      break;

   // checking some general conditions
   case isTheUserAuthenticated():
      redirectToLoginPage(); break;

   // checking variables
   case isPosted('title'):
      printError('The title is mandatory.'); break;
   case isPosted('text'):
      printError('The text is mandatory.'); break;
   case isPosted('category'):
      printError('The category is mandatory.'); break;

   // executing the process
   case connectToDatabase():
      printError('Database connection failed.'); break;
   case $article = Article::create(posted('title'), posted('text')):
      printError('The creation of the article failed!'); break;
   case moveArticleToCategory($article, posted('category')):
      printError('The category does not exist.'); break;
   default:
      printSuccess('Your article has been added to the category!');
}

MediaDoc : gestion de documents en mode wiki (PHP)

Le projet MediaDoc est né d’un besoin : pouvoir rapidement échanger des médias entre plusieurs personnes tout en associant à ces médias des informations de type documentation. L’idée est par exemple de pouvoir publier des codes sources, des extensions, etc. tout en permettant de construire simplement la documentation de ces fichiers.

Pour télécharger le projet dans l’état actuel : MediaDoc – Preview. Il s’agit d’une pré-version, non destinée à être utilisée dans un contexte de production. Elle n’est certainement pas exempte de bugs / failles de sécurité et a été soumise à des contraintes fortes (développement en une dizaine d’heures pour un seul fichier obligatoire), ce qui rend son code actuellement très sale et difficilement maintenable en l’état.

Pour l’utiliser, décompressez le fichier dans un dossier et c’est tout (normalement). Pas de connexion à une base de données, pas de configuration spéciale.

Le projet utilise par défaut :

  • de l’URL rewriting : supprimer/renommer le fichier .htaccess à la racine pour ne pas l’utiliser ;
  • de l’archivage des modifications : supprimer/renommer le dossier archives/ à la racine pour ne pas l’utiliser.

Une documentation succincte est fournie de base, mais en explorant un peu le contenu tout devrait être clair, je suppose.

Plugin jQuery : tronquage de texte

Le but de ce plugin jQuery est de tronquer le texte quand il fait plus d’une ligne. Typiquement, il s’agit de transformer :

Ceci est un texte beaucoup
trop long sur deux lignes.

En :

Ceci est un texte beaucoup...

Le plugin est téléchargeable à cette adresse.

Une démonstration est visible sur cette page.

L’intérêt du plugin est d’utiliser le plugin resize. Ainsi, lors du redimensionnement du texte (soit de la fenêtre, soit du parent ou autre), ce dernier est tronqué correctement.

Il est également possible de réafficher le texte complet au survol de la souris.

Pour l’utiliser, il suffit d’écrire :

$('selector').truncate(options);

Les options étant :

  • wrapZone : le pourcentage (entre 0 et 1) de largeur tronquée au maximum. Le plugin cherchant à tronquer entre les mots, si un mot est plus grand qu’un certain pourcentage de la largeur, cela risque de déformer le rendu. Ainsi, si on veut autoriser à tronquer au milieu d’un mot si ce dernier est plus grand que 20% de la largeur, on spécifiera wrapZone = 0.8 ;
  • endMarker : le marqueur de fin, “…” par défaut ;
  • onResize : active la gestion du tronquage lors du redimensionnement du noeud ;
  • displayOnOver : active l’affichage au survol du texte complet.

Plugin jQuery : “resize” event

Le plugin jQuery resize permet d’ajouter un évènement “resize” (de la même manière que “click” ou “mouseover”) aux objets jQuery (donc aux nœuds HTML). Vous pouvez le télécharger ici.

Il s’utilise de la même manière que les autres évènements :

$('monSelecteurCSS').resize(
	function(width, height, previousWidth, previousHeight) {
		// width: la largeur actuelle de l'élément
		// height: la hauteur actuelle de l'élément
		// previousWidth: la largeur de l'élément avant redimensionnement
		// previousHeight: la hauteur de l'élément avant redimensionnement
	});

Bientôt une application amusante sous la forme d’un autre plugin jQuery.

Pourquoi SVN est anti-Agile

Une politique courante en pratique Agile est d’avoir un tronc toujours compilable. Cela permet notamment à n’importe quel développeur qui veut reprendre le code de ne pas tomber sur des erreurs de compilation qu’il ne maîtrise pas et qui risquent de l’empêcher de tester ses propres modifications. Par extension, cela permet d’envisager l’utilisation de serveurs d’intégration qui permettent de tester en conditions réelles la compilation, le test et le déploiement du produit.

Un problème courant est de pouvoir vérifier avant enregistrement sur le tronc si la version est correcte. En effet, si le développeur omet par exemple d’ajouter au tronc des fichiers nécessaires, la compilation peut fonctionner sur son poste mais pas sur un autre environnement. Trouver d’où vient la source du problème peut même s’avérer long et fastidieux.

Une solution permettant de s’assurer que le tronc est toujours compilable est de faire intervenir dans le processus d’enregistrement des sources un serveur intermédiaire : le développeur enregistre localement ses modifications, les reporte sur le serveur à partir du gestionnaire de versions. Si la compilation est valide sur le serveur, il peut enregistrer les modifications sur le tronc.

Avec des gestionnaires de versions distibués (Bazaar, Git, Mercurial…), ce processus est facilement envisageable. Le développeur travaille sur son poste avec une branche locale du tronc. Une fois son travail effectué, il enregistre (commit local) les modifications puis les reporte sur le serveur (push). Quand la modification est validée, il peut reporter la modification sur le tronc (push, également).

Le cas de SVN ou des autres types de gestionnaires de versions est plus complexe à envisager. Il faut en effet créer tout d’abord une branche à partir du tronc, branche qui recevra les modifications à valider. Le développeur travaille sur cette branche (checkout, update) et y enregistre ses modifications (commit). Une fois validée, il les récupère à partir du serveur (checkout, update). Quand la modification est validée, il ne reste à faire qu’une fusion entre la branche et le tronc.

En somme, ce processus est adaptable mais nécessite plus d’efforts : les modèles de gestionnaires de versions distribués abstraient les branches et simplifient les passages de modifications entre branches par rapport aux modèles client-serveur.  Ces derniers rendent compliquée la tâche de reporter une modification d’une machine à une autre (d’un poste de développeur à un serveur d’intégration, par exemple). De manière générale, un gestionnaire de versions distribué s’adaptera mieux aux processus et politiques de développement.

À lire, au sujet des gestionnaires de versions et des processus :

Plugin jQuery pour l’affichage en clair des mots de passe au survol

Le problème a été relevé récemment dans plusieurs articles sur la sécurité : doit-on vraiment masquer les mots de passe dans les formulaires ?

Solution

Mot de passe en clair au survol

Mot de passe en clair au survol

La solution que je propose est relativement simple : lorsque le curseur de la souris survole le champ de mot de passe, celui-ci est affiché en clair. Sinon, il est masqué. L’explication est donnée en fin d’article.

Voici un premier jet de plugin jQuery qui permet de mettre en œuvre ce comportement :

(function($){
  $.fn.reveal = function() {
    this.filter('input[type="password"]').mouseover(function() {
      $(this).replaceWith(
        $('<input name="'+$(this).attr('name')+'" type="text" />')
          .attr('class', $(this).attr('class'))
          .val($(this).val())
          .mouseout(function() {
            $(this).replaceWith(
              $('<input name="'+$(this).attr('name')+'" type="password" />')
                .attr('class', $(this).attr('class'))
                .val($(this).val())
                .reveal()
            );
          })
      );
    });
    return this;
  };
})(jQuery);

Le plugin est relativement sommaire et peu complet (il correspond à une utilisation que j’en fait pour un projet personnel). Cependant, le principe est simple à comprendre et il est donc adaptable à d’autres situations.

Pour l’utiliser, il suffit d’appeler la fonction reveal() sur un objet jQuery (exemple : $('input.monPassword').reveal();).

Explication

Les arguments relevés dans l’article sont tout à fait judicieux : ne pas afficher le mot de passe est sujet à erreurs plus fréquentes et risque d’entraîner les utilisateurs à choisir des mots de passe plus faciles à taper. Des solutions plus ou moins bonnes ont été proposées, notamment par arc90 (HalfMask et HashMask). J’ai pu également lire une solution basée sur une case à cocher qui permettrait d’afficher ou masquer le mot de passe.

En ce qui me concerne, à l’exposé du problème j’ai pu identifier deux types d’utilisateurs : les utilisateurs novices ou standards, pour qui le masquage du mot de passe peut être un inconvénient et les utilisateurs avancés qui sont habitués à taper des mots de passe complexes correctement, et qui préfère généralement le masquage par défaut. La solution de la case à cocher est selon moi trop contraignante : le comportement par défaut risque d’agacer un des types d’utilisateurs.

En réfléchissant à la façon dont les deux types d’utilisateurs entraient un mot de passe (et plus généralement des données de formulaire), j’ai pu détacher un comportement relativement commun :  un utilisateur standard va très souvent cliquer sur le champ de formulaire pour l’indiquer tandis que l’utilisateur avancé va utiliser les contrôles clavier. Ainsi, la solution peut adopter un état par défaut (masquage ou affichage en clair) qui est deviné par rapport à ce comportement.

En l’occurrence, non seulement les utilisateurs standards cliquent sur le champ du formulaire, mais en général ils laissent le curseur dessus. C’est ce comportement précis qui dicte la solution expliquée plus haut.

Sans forcément être la solution idéale, je pense qu’elle a quelques avantages :

  • un état par défaut plus adapté au type d’utilisateur ;
  • un comportement relativement simple à comprendre et utiliser (plus qu’une case à cocher, a priori) ;
  • une réponse très générale, applicable à beaucoup d’autres cas d’utilisation ;
  • une sécurité une fois que l’utilisateur a compris le principe.

Le principal point faible est actuellement que ce comportement n’est pas naturel pour des personnes qui sont habituées à ne jamais voir leur mot de passe, et l’utilisateur standard est donc souvent surpris de lire son mot de passe en clair lorsqu’il lève les yeux du clavier. Une habitude est nécessaire pour que celui-ci pense à retirer la souris du champ du formulaire si une autre personne est présente dans la pièce.

(function($){
$.fn.reveal = function() {
this.filter(‘input[type="password"]‘).mouseover(function() {
$(this).replaceWith(
$(‘<input type=”text” name=”‘+$(this).attr(‘name’)+’” />’)
.attr(‘class’, $(this).attr(‘class’))
.val($(this).val())
.mouseout(function() {
$(this).replaceWith(
$(‘<input type=”password” name=”‘+$(this).attr(‘name’)+’” />’)
.attr(‘class’, $(this).attr(‘class’))
.val($(this).val())
.reveal()
);
})
);
});
return this;
};