ksort

lmaltier
Invité n'ayant pas de compte PHPfrance

09 août 2013, 14:06

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.

Mammouth du PHP | 2278 Messages

10 août 2013, 18:20

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);
?>
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

ViPHP
xTG
ViPHP | 7331 Messages

10 août 2013, 18:51

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 ? :)

Mammouth du PHP | 2278 Messages

11 août 2013, 11:27

maintenant, si on veut que l'ordre soit 1/2, 1, 2/1 il faudrait peut-être utiliser des cleis numériques?
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

lmaltier
Invité n'ayant pas de compte PHPfrance

03 sept. 2013, 08:02

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...

Mammouth du PHP | 2278 Messages

03 sept. 2013, 08:26

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...
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

lmaltier
Invité n'ayant pas de compte PHPfrance

03 sept. 2013, 08:42

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é...

Pitet
Invité n'ayant pas de compte PHPfrance

03 sept. 2013, 10:38

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

Mammouth du PHP | 2278 Messages

03 sept. 2013, 11:29

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...
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

Pitet
Invité n'ayant pas de compte PHPfrance

03 sept. 2013, 12:32

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.

ViPHP
xTG
ViPHP | 7331 Messages

03 sept. 2013, 13:42

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 ?

lmaltier
Invité n'ayant pas de compte PHPfrance

03 sept. 2013, 14:03

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