Page 1 sur 1

ksort

Posté : 09 août 2013, 14:06
par lmaltier
Bonjour,

Quelqu'un pourrait-il me dire ce qui m'échappe ? J'utilise la version PHP 5.4.3

Programme :

$a = array( '1/1' => 1, '1' => 1, '1/2' => 1);
ksort($a);
print_r($a);
ksort($a, SORT_STRING);
print_r($a);

résultat (illogique, on devrait avoir deux fois le même tableau, car par défaut, ksort est censé ne pas changer les types des clés, et les clés sont des chaînes de caractères) :

Array ( [1/2] => 1 [1] => 1 [1/1] => 1 ) Array ( [1] => 1 [1/1] => 1 [1/2] => 1 )

Merci de votre aide.

Re: ksort

Posté : 10 août 2013, 18:20
par sirakawa
mon test :
<?PHP
$a = array( "a" => 1, "c" => 1, "b" => 1);
$b = $a;
ksort($a);
print_r($a);
ksort($a);
print_r($a);
?>
conclut au bon fonctionnement quand il s'agit d'une "vraie" chaîne
et idem ici:
<?PHP
$a = array( "1" => 1, "1/2" => 1, "2/1" => 1);
$b = $a;
ksort($a);
print_r($a);
ksort($a);
print_r($a);
?>

Re: ksort

Posté : 10 août 2013, 18:51
par xTG
1/2 se trouve bien après 1/1 par rapport à leurs caractères ascii respectifs.

Si tu prends le caractère ascii X comme étant celui de 1 et Y comme étant celui de / alors voilà les chaînes que nous avons :
1/1 = X Y X
1/2 = X Y (X+1)

X+1 est bien après X non ? :)

Re: ksort

Posté : 11 août 2013, 11:27
par sirakawa
maintenant, si on veut que l'ordre soit 1/2, 1, 2/1 il faudrait peut-être utiliser des cleis numériques?

Re: ksort

Posté : 03 sept. 2013, 08:02
par lmaltier
Non, je veux bien des clés chaînes de caractères, le résultat de ksort($a, SORT_STRING); qui est Array ( [1] => 1 [1/1] => 1 [1/2] => 1 ), est donc parfaitement correct.

Mon problème est que, si je ne mets pas le paramètre SORT_STRING, le tri est incorrect, alors qu'il devrait être strictement identique avec ou sans le paramètre puisque les clés sont dans les deux cas des chaînes de caractères. J'ai perdu pas mal de temps avec ça, et j'ai donc trouvé le contournement (mettre SORT_STRING), mais le comportement sans le SORT_STRING est tout à fait anormal, à première vue. Je sais que ça semble énorme et invraisemblable comme bug PHP, et pourtant...

Re: ksort

Posté : 03 sept. 2013, 08:26
par sirakawa
Par défaut c'est SORT_REGULAR : compare les éléments normalement (ne modifie pas les types)
Est-ce que "1/2" malgré les quotes est considéré comme un nombre? je ne saurais dire...

Re: ksort

Posté : 03 sept. 2013, 08:42
par lmaltier
Exactement, et c''est aussi l'hypothèse que j'ai faite. Mais c'est quand même incroyable... Si quelqu'un pouvait essayer avec la dernière version de PHP disponible pour voir si c'est encore la même chose ou si le problème est réglé...

Re: ksort

Posté : 03 sept. 2013, 10:38
par Pitet
Salut,

Essayez ceci :
$a = array( "1/1" => 1, "1" => 1, "1/2" => 1);

foreach ($a as $key => $value) {
	print_r(gettype($key));
}
La clé "1" est directement convertie en integer à l'affectation du tableau.
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
(http://php.net/manual/en/language.types.array.php)

Puisque par défaut ksort() ne modifie pas les types des clés, il va devoir comparer une clé de type string avec un clé de type integer, c'est à ce moment que les clés string sont converties en numérique selon le principe de la conversion des chaines en nombres (http://www.php.net/manual/fr/language.t ... conversion).

Le comportement de ksort() est donc normal puis le soucis vient de l'affectation du tableau.

En modifiant la clé "1" par "01", on retrouve le comportement attendu.
<?php
$a = array( "1/1" => 1, "01" => 1, "1/2" => 1);

foreach ($a as $key => $value) {
print_r(gettype($key));
}

ksort($a, SORT_REGULAR);
print_r($a);
ksort($a, SORT_STRING);
print_r($a);

?>


Bonne journée

Re: ksort

Posté : 03 sept. 2013, 11:29
par sirakawa
Une fois de plus, le typage mou de PHP montre toutes ses faiblesses, d'autant qu'on ne voit pas pourquoi "01" ne serait pas un nombre...

Re: ksort

Posté : 03 sept. 2013, 12:32
par Pitet
Le typage faible de PHP n'est pas une faiblesse, il faut savoir l'utiliser correctement.

"01" est bien un nombre, mais ce n'est pas un entier : le 0 en premier caractère indique une notation octale (http://www.php.net/manual/en/language.types.integer.php).

Même si certains comportement de PHP nous semble étrange, ils sont tout à fait corrects tant que la documentation officielle les justifie.

Re: ksort

Posté : 03 sept. 2013, 13:42
par xTG
Une fois de plus, le typage mou de PHP montre toutes ses faiblesses, d'autant qu'on ne voit pas pourquoi "01" ne serait pas un nombre...
"01" est une string jusqu'à preuve du contraire, ce serait pour le coup plus une preuve de typage fort si on voulait être ironique. ;)
01 est un entier en base 8.
1 est un entier en base 10.

L'exécution de :
$a = array( "1/1" => 1, "01" => 1, "1/2" => 1);

foreach ($a as $key => $value) {
print_r(gettype($key));
}
Affichera string. ;)

Et donc tout tri sera effectué sur le code ASCII des caractères si on ne demande pas un cast en entier.
Donc pourquoi ne pas utiliser le flag SORT_NUMERIC puisqu'il est question de cast en nombre ?

Re: ksort

Posté : 03 sept. 2013, 14:03
par lmaltier
Merci beaucoup pour ces rappels. Je suis surpris, parce que dans mon cas réel, je n'avais pas rempli le tableau à la main, bien sûr, et j'avais vérifié le type string des clés au cours de mes tests. Mais je n'avais peut-être pas vérifié pour absolument tous les éléments du tableau. C'est donc certainement l'explication, il y avait bien quelque chose qui m'échappait. On peut même comprendre que la clé '1/2' se retrouve avant la clé '1/1' (alors qu'elle devrait être après), et que la clé '1' (devenue entière) puisse être entre les deux : en effet, en cas de comparaison entre un nombre et un string, le string est converti en nombre avant la comparaison. Il reste que :
- c'est très trompeur de se retrouver avec un tri complètement en numérique flottant alors qu'on n'avait utilisé au départ que des strings.
- la doc est mal écrite : il faudrait écrire non pas "Strings containing valid integers will be cast to the integer type." mais "Strings containing valid integers written in the decimal notation will be cast to the integer type."