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"
}

Publié par Olivier Balais

Jeune ingénieur logiciel basé à Lyon (@overnetcity) passionné par les NTIC et le développement Web, je suis actuellement salarié chez Reputation VIP et effectue en parallèle des missions ponctuelles en temps que Freelance. Passionné depuis toujours par l'informatique et le développement, suite à une formation solide à l'INSA de Lyon, je me suis spécialisé dans la réalisation de bout en bout de projets web complexes.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *