Une situation somme toute banale : vouloir connaître le nombre de caractères contenus dans une variable, quelle que soit la raison. Pour ce faire, il existe, en PHP, la fonction strlen qui permet de calculer la taille d'une chaîne.
TL;DR
Utilisez mb_strlen($var);
Exemples
echo strlen('Bonjour !'); // 6
echo strlen('Je suis le roi du monde !'); // 25
echo strlen("Ça m'étonnerait bien !"); // 24 (ça ne devrait pas être 22 ?)
echo strlen('⌐■_■'); // 10 (et ici, il n'y a que 4, non ?)
Ah ! Mais pourquoi les deux derniers exemples renvoient des résultats faux ??
Encodage
En fait, le résultat en soi n'est pas faux, la fonction est censée calculer la taille d'une chaîne, et le fait bien. C'est une histoire d'encodage, un bouzin complexe que beaucoup de développeurs ne maîtrisent pas complétement.
Je ne rentrerai pas dans les détails techniques, mais en gros cela permet de stocker toute sorte de caractères (alphabets russe, chinois ou arabe, hiéroglyphes, smiley, etc.) imprimable à l'écran. Pour le voyant, il verra une lettre ou un smiley ; mais au cœur du système, chaque caractère est stocké sur plusieurs octets. Du coup, la simple lettre ç prend 2 octets (donc 2x plus de place) mais on ne compte qu'une seule lettre.
Pour le coup, ⌐■_■
est formé de 4 caractères, mais prend 10 octets en mémoire, d'où le résultat de strlen.
Contournements
Si vous voulez tout simplement connaître le nombre de lettres, sans vous occuper de l'encodage, il y a cette astuce :
echo strlen(utf8_decode("Ça m'étonnerait bien !")); // 22
echo strlen(utf8_decode('⌐■_■')); // 4
utf8_decode permet de convertir tous les caractères UTF-8 en ISO-8859-1, où chacun occupe une seule place en mémoire, comme cela nous paraît logique.
Voici la tête de nos deux exemples, après conversion :
echo utf8_decode("Ça m'étonnerait bien !"); // "�a m'�tonnerait bien !"
echo utf8_decode('⌐■_■'); // "??_?"
Du coup, strlen peut calculer les bonnes longueurs, tel qu'on le conçoit dans nos p'tites têtes.
Mise à jour suite aux commenaires 1 et 2 : la méthode la plus appropriée (et faite pour) est d'utiliser mb_strlen.
Voici un benchmark de 3 méthodes fonctionnelles :
$string = '⌐■_■';
$t1 = microtime(1);
for ( $i = 0; $i < 1000000; ++$i ) {
$len = strlen(utf8_decode($string));
}
$t2 = microtime(1);
for ( $i = 0; $i < 1000000; ++$i ) {
$len = mb_strlen($string);
}
$t3 = microtime(1);
for ( $i = 0; $i < 1000000; ++$i ) {
$len = preg_match_all('(.)su', $string, $matches);
}
$t4 = microtime(1);
echo 'strlen(utf8_decode()) : ', ($t2-$t1), PHP_EOL;
echo 'mb_strlen() : ', ($t3-$t2), PHP_EOL;
echo 'preg_match_all() : ', ($t4-$t3), PHP_EOL;
/*
strlen(utf8_decode()) : 0.30508279800415
mb_strlen() : 0.16338109970093
preg_match_all() : 1.5465741157532
*/
Conclusion
Utilisez mb_strlen($var)
.