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 :
- ‘90000000000000000001’
- ‘90000000000000000002’
- ‘90000000000000000003’
Ce n’est malheureusement absolument pas le cas :
- ‘90000000000000000002’
- ‘90000000000000000003’
- ‘90000000000000000001’
Pire, si on re-exécute un sort sur ce tableau, on obtient un ordonnancement différent :
- ‘90000000000000000001’
- ‘90000000000000000003’
- ‘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 :
- 9.0E+19
- 9.0E+19
- 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" }