Problème avec PHP et ksort / asort / sort sur les strings représentant de grands entiers

La semaine passée, en essayant de trier des tableaux avec la fonction sort de PHP, je suis tombé sur un résultat assez surprenant :

php> $array = array('90000000000000000001', '90000000000000000003', '90000000000000000002');

php> var_dump($array);
array(3) {
  [0]=>
  string(20) "90000000000000000001"
  [1]=>
  string(20) "90000000000000000003"
  [2]=>
  string(20) "90000000000000000002"
}

php> sort($array);

php> var_dump($array);
array(3) {
  [0]=>
  string(20) "90000000000000000002"
  [1]=>
  string(20) "90000000000000000003"
  [2]=>
  string(20) "90000000000000000001"
}

Dans la trace ci-dessus, on constate que mon tableau contient trois chaînes ‘90000000000000000001’, ‘90000000000000000003’ et ‘90000000000000000002’ qui devraient donc, a priori, être triées dans cet ordre après un appel à sort :

  1. ‘90000000000000000001’
  2. ‘90000000000000000002’
  3. ‘90000000000000000003’

Ce n’est malheureusement absolument pas le cas :

  1. ‘90000000000000000002’
  2. ‘90000000000000000003’
  3. ‘90000000000000000001’

Pire, si on re-exécute un sort sur ce tableau, on obtient un ordonnancement différent :

  1. ‘90000000000000000001’
  2. ‘90000000000000000003’
  3. ‘90000000000000000002’

Il s’avère en fait que PHP interpréte cette chaîne comme un entier. Et étant donné que PHP gère assez mal les large numbers, il se retrouve en fait à faire, dans le cas présent, un sort sur les valeurs suivantes :

  1. 9.0E+19
  2. 9.0E+19
  3. 9.0E+19

Les trois chaînes du tableau sont donc converties en entier puis arrondies et PHP les considèrent dès lors comme identiques. Ce qui explique le sorting foireux !
La solution dans ce genre de cas est de forcer PHP à interpréter les valeurs de ce tableau comme des chaînes et non pas comme des entiers. Pour cela :

php> sort($array, SORT_STRING);

Mais encore faut-il qu’on sache exactement à l’avance, le type de valeurs qu’on veut trier…

Une autre solution apportée par @mageekguy est d’utiliser la fonction natsort qui répondrait plus correctement au présent problème :

php> $array = array('90000000000000000001', '90000000000000000003', '90000000000000000002');

php> var_dump($array);
array(3) {
  [0]=>
  string(20) "90000000000000000001"
  [1]=>
  string(20) "90000000000000000003"
  [2]=>
  string(20) "90000000000000000002"
}

php> natsort($array);

php> var_dump($array);
array(3) {
  [0]=>
  string(20) "90000000000000000001"
  [1]=>
  string(20) "90000000000000000002"
  [2]=>
  string(20) "90000000000000000003"
}

Equivalent de svn:externals avec Git – Comment intégrer un dépôt externe à son propre projet

N’utilisant Git que depuis quelques mois, je n’en connais pas encore tous les aspects, loin de là !
Aujourd’hui j’ai eu un besoin très simple, intégrer à mon dépôt Git un autre dépôt Git. En gros, l’équivalent d’un bon vieux svn:externals. Je n’ai pas trouvé immédiatement comment faire donc je partage ici la commande magique :

git submodule

L’utilisation est donc très simple. Au lieu de faire un git clone dans un sous-dossier de son projet, il suffit de faire à la racine du dépôt :

git submodule add repo_url ./your_folder/

Un nouveau dossier caché est créé. Il vous suffit donc de le versionner dans votre propre dépôt pour que la référence soit prise en compte.

Si vous clonez votre dépôt depuis un nouvel emplacement, il vous faut utiliser la commande git submodule init pour récupérer les dépôts externes référencés.
Et ensuite, un git submodule update pour mettre à jour régulièrement les sous-projets.

Aussi simple que ça !

Plus d’infos sur cette commande à l’adresse suivante : http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html

Premiers pas avec Node.js – Installation de Node et Express

Edit du 26/11/2012

Depuis la rédaction de cet article, nodejs et npm ont beaucoup évolué (A l’époque, node était en version 0.4, il est désormais en 0.8 !).
La manière la plus simple de les installer à mon sens est de passer par le package manager de sa distribution.
Sous Ubuntu :

sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs npm

RDV ici pour plus d’infos : https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager

Article d’origine

Chez PMSIpilot, à chaque fin de Sprint, les équipes techniques organisent et participent à ce qu’on appelle les « ateliers ». Comme tout bon geek qui se respecte, j’ai une soif insatiable d’apprendre de nouvelles choses et je participe donc en général au plus grand nombre possible de ces ateliers. Un de ceux qui m’ont le plus marqué est sans aucun doute celui qui présentait le fonctionnement et l’utilisation de Node.js.

Je vais donc profiter de ce blog pour faire un retour au fur et à mesure de ma découverte de Node.js et de son écosystème.
Ce premier billet résume l’installation de Node et Express et me servira de mémo pour retrouver facilement les commandes qui vont bien.

Node.js est un framework très en vogue qui consiste à utiliser le langage Javascript avec le moteur V8 de Google côté serveur pour servir les requêtes HTTP reçues.

Parmis les intérêts majeurs de ce type d’approche on retient notamment :

  • L’approche évènementielle du framework (entrées / sorties non bloquantes…)
  • La possibilité d’utiliser un seul langage côté serveur et côté client pour les applications Full JS

Ainsi, grâce à sa légèreté et à ses I/O non bloquants, Node.js permet de développer une application web capable de recevoir et de répondre à un nombre de requêtes infiniment plus conséquent qu’une même application développée avec une pile LAMP classique.
C’est toute la philosophie du développement web qui se voit bouleversée par cette approche évènementielle.

Pour plus d’informations à ce sujet, je vous invite à lire les articles suivants :

Let’s get started!

Première étape, télécharger et installer Node.js. Pour se faire RDV sur le site officiel pour télécharger la dernière release en date : http://nodejs.org/#download. Décompressez l’archive, rendez-vous dans le dossier et exécutez les commandes suivantes une par une :

mkdir ~/local
./configure --prefix=$HOME/local/node
make
make install
export PATH=$HOME/local/node/bin:$PATH

La commande make prend un certain temps à s’exécuter donc il faut s’armer de patience. Ensuite le reste est trivial.
A noter que si au moment du lancement de configure, vous avez une erreur du type Could not autodetect OpenSSL support. Make sure OpenSSL development packages are installed, c’est qu’il vous manque la lib OpenSSL.
Dans ce cas, un petit apt-get résoud le problème :

sudo apt-get install libssl-dev

Dans le même ordre d’idée, il se peut qu’un message vous avertisse qu’il manque un compilateur du type g++ / c++. Dans ce cas :

sudo apt-get install g++

Voilà une bonne chose de faite. Testez l’installation en exécutant la commande suivante :

obalais@server:~$ node -v
v0.4.7

Même s’il est possible de créer une application web complète juste avec Node.js, nous allons installer des plugins qui vont nous faciliter le développement. Un framework MVC (Express) par exemple me semble plus que nécessaire, un moteur de template (Jade) ainsi qu’un ORM pour la base de données (Mongoose si on utilise une base MongoDb).
Nous avons de la chance puisqu’un gestionnaire de paquets spécifique à Node.js est devenu plus ou moins un standard pour récupérer facilement les plugins qui existent, à savoir NPM.

Si vous n’avez pas curl :

sudo apt-get install curl

Ensuite, l’installation de NPM est on ne peut plus simple puisqu’il suffit d’une seule ligne :

curl http://npmjs.org/install.sh | sh

Et une fois de plus, vous pouvez tester si l’installation s’est bien déroulée en lançant la commande suivante :

obalais@server:~$ npm -v
1.0.6

Nous allons tout de suite mettre à profit NPM en installant Express, le framework de développement web le plus abouti actuellement pour Node.js.

Voici la liste de ses features, directement récupérée depuis le site officiel :

  • Robust routing
  • Redirection helpers
  • Dynamic view helpers
  • Application level view options
  • Content negotiation
  • Application mounting
  • Focus on high performance
  • View rendering and partials support
  • Environment based configuration
  • Session based flash notifications
  • Built on Connect
  • Executable for generating applications quickly
  • High test coverage

Grâce à NPM, l’installation se fait encore une seule ligne toute simple :

npm install express

N’oubliez pas de rajouter express à votre PATH et de tester le résultat de l’installation :

obalais@server:~$ export PATH=$HOME/node_modules/express/bin:$PATH
obalais@server:~$ express --version
2.3.4

Maintenant installez Jade :

npm install jade

Tout est en place. Il ne reste plus qu’à créer notre serveur avec Node.js + Express. Pour cela, créez un fichier helloworld.js contenant le code suivant :

var app = require('express').createServer();

app.get('/', function(req, res){
  res.send('<h1>another hello world</h1>');
});

app.listen(3000);

On lance le serveur node.js avec la commande suivante :

node helloworld.js

Et on teste si tout fonctionne correctement en se rendant via son navigateur sur son localhost sur le port 3000.

Nous verrons dans un prochain article quels sont les principes fondamentaux du développement avec Express et Node.js.
En attendant, je vous laisse visiter le site officiel d’Express qui contient toute la documentation nécessaire pour se lancer dans l’utilisation du Framework. Faites un tour sur cette page pour commencer.

sfForms et valeurs par défaut. Pourquoi est-ce que getDefault me renvoie null ?

Les formulaires de symfony ont quelquefois des comportements surprenants pour le développeur. Tout le monde pense immédiatement aux embed forms mais ce n’est pas de ces derniers dont je vais parler ici.

Je suis tombé sur le problème suivant : pourquoi après avoir passé une valeur par défaut à un des widgets de mon formulaire, je ne parviens pas à la récupérer dans mon action ou dans mon template ? L’appel à la méthode getDefault du sfForm me renvoie null, tout simplement.

Cas typique :


// Dans le code de mon formulaire :
$this->setWidgets(array(
  ...
  'my_field' => new sfMyWidget(array('default' => 'my_value')),
));

// Dans le code de mon action :
if ($my_form->getDefault('my_field') == 'some_value')
{
  ...
}

Dans ce cas, la méthode getDefault() appelée sur mon formulaire me renvoie null alors que j’ai explicitement donné une valeur par défaut à mon widget. Pourtant, en lisant la doc de symfony sur le sujet je retiens ceci :

The setDefault(), getDefault(), setDefaults(), and getDefaults() methods manages the default values for the embedded widgets. They are proxy methods for the getDefault() and setDefault() widget methods.

Les méthodes setDefault() et getDefault() au niveau de la classe sfForm sont censées êtres des raccourcis pour les méthodes setDefault() et getDefault() de la classe sfWidgetForm().

En fait il s’avère que ce n’est absolument pas le cas et que la méthode getDefault() du formulaire ne fait jamais appel à la méthode getDefault() du widget associé.

L’appel à $my_form->getDefault(‘my_field’) n’est donc pas équivalent à $my_form[‘my_field’]->getWidget()->getDefault().

Par conséquent, il est important de bien comprendre quelle méthode appeler et dans quel cas.

A savoir, $my_form->getDefault(‘my_field’) quand le setDefault a été utilisé au niveau du formulaire. Exemple :


// Valeurs passées dans mon formulaire :
$this->setWidgets(array(
  ...
  'my_field' => new sfMyWidget(),
));
$this->setDefault('my_field', 'my_value');

// Ou alors valeurs passées dans mon action :
$my_form = new myFormClass(array(
  ...
  'my_field' => 'my_value',
));

Et, $my_form[‘my_field’]->getWidget()->getDefault() quand le setDefault() a été utilisé au niveau du widget. Exemple :


// Dans le code de mon formulaire :
$this->setWidgets(array(
  ...
  'my_field' => new sfMyWidget(array('default' => 'my_value')),
));

Voilà, désormais vous êtes prévenus…