Comment PHP représente-t-il en interne les chaînes?

18

UTF8?
UTF16?

Les chaînes en PHP conservent-elles également l'encodage utilisé?

Regardons ce script par exemple. Dis que je cours:

$original = "शक्नोम्यत्तुम्";

Que se passe-t-il réellement?

Évidemment, je pense $originalqu'il ne contiendra pas seulement 7 caractères. Ces glyphes doivent chacun y être représentés par plusieurs octets.

Alors je fais:

$converted = mb_convert_encoding ($original , "UTF-8");

Que va-t-il arriver $converted? En quoi sera-t-il $converteddifférent de $original?

Sera-ce exactement la même séquence d'octets, $originalmais avec un codage différent?

user4951
la source
1
Quelle version de PHP? PHP <6 ne peut pas gérer UTF-8 natif. Il existe cependant des packages et des méthodes qui aident / résolvent ce problème. Google s'amuse avec utf-8 et php. Passez ensuite à une autre plate-forme au lieu de PHP. :)
Andrew T Finnell
4
PHP <6? Cela inclurait toutes les versions de PHP jamais publiées ...
tdammers
1
De plus, PHP peut gérer UTF-8, il n'a tout simplement pas de type de données dédié, vous devez donc regarder ce que vous faites.
tdammers

Réponses:

22

Une chaîne PHP n'est qu'une séquence d'octets, sans aucun encodage qui lui soit associé. Les valeurs de chaîne peuvent provenir de diverses sources: le client (via HTTP), une base de données, un fichier ou des littéraux de chaîne dans votre code source. PHP lit tout cela sous forme de séquences d'octets, et il n'extrait jamais aucune information d'encodage.

Tant que toutes vos sources et destinations de données utilisent le même encodage, le pire qui puisse arriver est que les positions des chaînes sont incorrectes (si vous utilisez des encodages multi-octets), car PHP comptera les octets, pas les caractères.

Mais si les encodages ne correspondent pas (par exemple, vous écrivez un littéral de chaîne dans un fichier source stocké en UTF-8, puis l'envoyez à une base de données qui attend Latin-1), PHP n'effectuera aucune conversion pour vous: il le fera heureusement copier les octets sur raw.

La solution la plus saine est la suivante:

  • Réglez l'encodage interne de PHP sur UTF-8.
  • Enregistrez tous vos fichiers source au format UTF-8.
  • Utilisez UTF-8 comme encodage de sortie (n'oubliez pas d'envoyer des en- Content-typetêtes appropriés ).
  • Définissez la connexion à la base de données pour utiliser UTF-8 ( SET NAMES UTF8dans MySQL).
  • Configurez tout le reste pour être UTF-8 si possible.
  • Pour tout ce que vous ne pouvez pas contrôler (par exemple, les services Web tiers), assurez-vous de connaître l'encodage et convertissez-le en UTF-8 le plus tôt possible, puis revenez à l'autre encodage le plus tard possible.

Pourquoi UTF-8? Parce qu'il peut représenter tous les caractères Unicode et remplace ainsi tous les encodages 7 bits et 8 bits existants, et parce qu'il est compatible binaire avec ASCII, c'est-à-dire que chaque chaîne ASCII valide est également une chaîne UTF-8 valide (mais pas vv .).

Dans votre exemple, ce qui se passe est le suivant.

Tout d'abord, vous enregistrez votre fichier source; votre éditeur de texte est probablement configuré pour utiliser UTF-8, donc votre chaîne littérale finit par être codée UTF-8 sur le disque. PHP lit ce fichier, interprétant la chaîne comme une série d'octets; $originalcontient désormais une chaîne codée en UTF-8 de 7 caractères, qui n'est qu'une séquence d'octets (bien qu'elle contienne plus de 7 octets, car chaque caractère est représenté par deux octets ou plus). Si vous appelez ensuite echo $original, la chaîne encodée est envoyée au client en l'état; si vous avez dit au client de s'attendre à UTF-8, tout va bien, mais si ce n'est pas le cas, PHP n'a aucun moyen de faire la différence, et vous vous retrouverez avec des ordures dans le navigateur. À titre expérimental, essayez ceci:

$original = "शक्नोम्यत्तुम्";
echo strlen($original);

strlen est indépendant du codage et suppose un codage à 8 bits de largeur fixe, c'est-à-dire un octet par caractère, donc il comptera les octets, pas les caractères.

tdammers
la source
Donc $ converti représentera la même chaîne mais dans un autre encodage. L'encodage brut réel, qui est le magasin PhP, sera différent.
user4951
2
Je vais le répéter pour vous: PHP stocke des octets, pas des caractères, et il ne connaît pas du tout les encodages (bien que certaines fonctions de bibliothèque le fassent.
tdammers
1
Oh, et c'est "PHP", pas "PhP".
tdammers
2
si les octets bruts sont les mêmes, quelle est la différence entre $ original et $ converti alors. C'est ce que je demande.
user4951
2
Oh, OK, c'est ce que tu veux dire. Oui, les octets bruts changent en fonction de la conversion d'encodage. PHP ne se souvient pas du codage, donc si vous convertissez une chaîne de, disons, utf-8 en latin-1, puis traitez le résultat comme utf-8, vous verrez des résultats étranges.
tdammers