PHP et UTF-8 : les frères ennemis font la paix

PHP est un vieux langage. Apparu aux tous débuts de l’ère Internet, en 1994, il a depuis beaucoup évolué. La dernière grande évolution est apparue avec la version 5, le 13 juillet 2004, apportant le support natif de vraies classes et objets, comme tout langage évolué. La dernière nouveauté, la gestion des espaces de noms, est apparue avec la version 5.3, le 30 juin 2009. Depuis le 22 juillet 2010, nous sommes en version 5.3.3.

Pourtant, malgré cette évolution constante, certains points techniques majeurs ne sont pas traités comme il faut. Je n’en citerai que deux :

  • Le nommage des fonctions et l’ordre des paramètres n’est pas toujours cohérent,
  • Le langage supporte en natif l’encodage ISO-8859-1 et pas UTF-8.

Le premier sujet est traité par l’excellent article de Armel Fauveau : « Et si PHP était audité demain ? ». Je vous invite à le lire. L’article semble caricatural, mais tout ce qui y est dit est vrai. Je rajouterai seulement qu’en plus de tout cela, les fonctions ne sont pas intégrées dans des classes.

Le second sujet est plus problématique. L’Internet est mondial. Ne supporter que l’encodage latin là où tous les autres langages sont passés à Unicode, et donc UTF-8, fait passer PHP comme un langage d’hier dont l’avenir est incertain. La version 6 devait apporter une solution radicale en supportant nativement Unicode. Mais voilà. Il n’existe aucune roadmap pour cette version. Pis, la branche de développement PHP 6 a été supprimée ! J’ai même lu, au hasard de mes surfs, un article qui titrait « PHP 6 est mort ! Vive PHP ! ».

Il faut donc faire avec les moyens du bord. Comment faire de l’UTF-8 avec PHP 5 ? Voici un guide pratique.

Un petit rappel sur les encodages

Au début était l’ASCII. 1961, cela ne nous rajeunit pas. L’ASCII est une norme de codage de caractères qui définit 128 valeurs pour représenter 128 caractères. Par convention, « A » correspond à la valeur 65.

Oui, mais voilà, les caractères accentués ne sont pas dans cette liste. Cela ne pose pas de problèmes aux Américains, mais aux Européens occidentaux, si. C’est ainsi que l’ISO-8859-1, ou Latin1, est né. Cette norme de codage reprend les 128 premiers caractères de l’ASCII pour lui rester compatible et rajoute un ensemble de valeurs entre les positions 160 et 255, dont les accents.

Microsoft a de son côté comblé le trou entre les positions 128 et 160 via sa norme de codage Windows-1252.

Avec tout cela, les Européens occidentaux sont contents. Et les autres ? Qu’à cela ne tienne. ISO-8859-3 est créé pour les Turcs, ISO-8859-4 pour les Lettons, ISO-8859-5 pour les Russes, ISO-8859-6 pour les Arabes, ISO-8859-7 pour les Grecs, ISO-8859-8 pour les Israéliens, ISO-8859-9 pour les Turcs (encore), ISO-8859-10 pour les pays nordiques, ISO-8859-11 pour les Thaïlandais, ISO-8859-13 pour les Baltes, ISO-8859-14 pour les Celtes, ISO-8859-15 pour les Européens lorsqu’ils sont passés à l’euro et ISO-8859-16 pour les pays de l’Est.

On satisfait plus de monde, mais comme ce sont toujours les mêmes codes qui sont remplacés, il est impossible d’écrire un texte en utilisant les caractères de plusieurs langues.

En plus, il faut régler le problème chinois dont chaque mot représente un idéogramme différent. Ainsi, la norme de codage GB2312 code 8192 caractères sur deux octets et BIG5 un nombre équivalent, mais de manière différente.

Tout cela n’est pas cohérent. C’est ainsi qu’a été créé Unicode. Son objectif ? Affecter aux 65 535 valeurs, que l’on peut obtenir avec 2 octets, tous les caractères du monde. En étant compatible Unicode, on est compatible avec le monde entier.

UTF-8 est un dérivé d’Unicode qui permet de rester compatible ASCII pour les 128 premières valeurs et utiliser de 2 à 5 octets pour les autres valeurs, avec comme règle que les caractères à 5 octets correspondent à ceux qui sont les moins utilisés dans le monde.

Les interactions de PHP avec le monde extérieur

Pour gérer un site PHP en UTF-8, il faut que toutes les interactions avec le monde extérieur se fassent en UTF-8. Quand je parle de monde extérieur, j’entends les interactions Ajax avec JavaScript, l’affichage de pages HTML, la transmission de formulaires HTML, l’accès aux bases de données, aux fichiers et à XML. Prenons-les dans l’ordre.

PHP et JavaScript

JavaScript travaille nativement en Unicode. Toutes les requêtes Ajax et les réponses doivent être en UTF-8. Si l’on travaille en UTF-8 en PHP, il n’y a pas à s’inquiéter de ce côté.

PHP et HTML

Pour qu’une page HTML soit affichée en UTF-8, il faut :

  • Indiquer dans l’entête html que la page sera en UTF-8 via le code HTML :
    <meta http-equiv= »Content-Type » content= »text/html; charset=UTF-8″ />
  • Indiquer dans l’entête http que la page sera en UTF-8 via la commande PHP :
    header(« Content-Type: text/html; charset=UTF-8 »);
  • Au lieu de l’indiquer dans l’entête http, vous pouvez également le déclarer dans votre configuration Apache, fichier httpd.conf si vous y avez accès ou dans un fichier .htaccess à la racine de votre site. La commande à ajouter est :
    AddDefaultCharset UTF-8

PHP et la base de données MySql

La base de données MySql doit également fonctionner nativement en UTF-8. Quand vous créez vos bases et vos tables, vous devez le faire avec le paramètre d’interclassement utf8_general_ci.

Cela peut être vérifié via l’interface PhpMyAdmin. L’écran de connexion permet de choisir la langue et l’encodage de l’interface. Bien entendu, il est préférable de choisir UTF-8.

Après avoir cliqué sur le nom de votre base de données dans la liste de gauche, puis l’onglet Operations, un paragraphe Interclassement apparait. Si la valeur n’est pas utf8_general_ci , changez-la.

Cliquez à nouveau sur le nom de votre base dans la liste de gauche. Dans l’onglet par défaut Structure, la liste des tables créées est affichée. Si une des tables n’a pas l’interclassement utf8_general_ci, cliquez sur l’icône Propriétés de cette table puis l’onglet Opérations. L’écran vous permet de changer l’interclassement. Il faudra également modifier l’interclassement des attributs de type texte de la table. La manière d’opérer est similaire.

Attention : le changement d’interclassement ne change pas les données présentes dans les tables. Si des données non UTF-8 et non ASCII existent,  il va falloir envisager une procédure de conversion plus complexe.

Il vous reste ensuite à indiquer que la communication PHP – MySql se fera en UTF-8. Il suffit d’envoyer la commande SQL « SET NAMES ‘utf8’ » avant toute requête. Vous l’insérerez dans votre code PHP, juste après l’ordre de connexion à la base :

$db  = new PDO($dsn, $user, $password);
$db->query(« SET NAMES ‘utf8’ « );

PHP et les fichiers

La lecture des fichiers XML ne pose pas de problème, XML doit être en UTF-8. Les fichiers d’images sont binaires. Il n’y a donc pas d’encodage. Il reste à traiter les fichiers texte dont le contenu n’est pas au format UTF-8.

Nous nous sommes jusque là passé des fonctions utf8_encode et utf8_decode. Ce sera uniquement dans ces rares cas qu’il faudra les utiliser. La première permet la conversion d’une chaîne de caractères ISO-8859-1 en UTF-8. La seconde permet l’inverse.

Dans tous les autres cas, il ne faudra surtout pas utiliser ces fonctions. En particulier, vous ne devez pas appliquer deux fois successivement ces fonctions. Le résultat obtenu ne sera pas ce que vous espérez car il n’y a pas de système de reconnaissance des données source.

PHP et les noms de fichiers

Quand vous utilisez l’explorateur Windows, les noms des fichiers ne sont pas encodés en UTF-8 mais en Windows-1252. Si vous obtenez un nom de fichier à manipuler via JavaScript ou une requête de formulaire, vous devrez convertir le nom du fichier via les fonctions citées dans le paragraphe précédent. A moins que vous utilisiez directement le codage UTF-8 ce qui fait qu’ils apparaîtront bizarrement dans l’explorateur Windows.

Et dans PHP lui-même ?

Une fois toutes ces précautions prises, il ne reste plus grand-chose à faire côté PHP. Toutefois, vous devez bannir l’utilisation des fonctions de manipulation de chaînes de caractères, en gros la majorité de celles qui commencent par str. La majorité, mais pas toutes : strcmp peut par exemple être utilisé. En remplacement, vous devez utiliser les mêmes fonctions préfixées de mb_ à savoir, mb_strlen, mb_strpos, mb_substr. Elles ont les mêmes paramètres si vous avez indiqué préalablement au module mb que vous travaillez en UTF-8. La fonction ci-après est à placer au début de votre script :

mb_internal_encoding(« UTF-8 »);

Est-il possible de connaître l’encodage d’une chaîne de caractères ?

Pour savoir si l’on doit utiliser utf8_encode, utf8_decode ou pas, il aurait été bien de connaître l’encodage de la chaîne de caractères. La fonction mb_detect_encoding paraissait toute indiquée. Malheureusement, mes multiples essais ont montré son incapacité à répondre au besoin. Il existe toutefois dans la documentation de cette fonction sur le site php.net des contributions d’utilisateurs qui proposent des fonctions qui pourraient répondre à ce besoin. Ce n’est en tout cas pas géré nativement par PHP.

PHP et les éditeurs de fichiers

Vous êtes près du but. Maintenant que tout est en place, vous êtes prêt à coder en UTF-8. Mais il faut que vous éditiez vos fichiers au format UTF-8 sans BOM. BOM est une entête de 2 caractères qui permet de dire que le fichier est au format UTF-8. Si vous laissez ce BOM dans votre fichier, Apache va croire que c’est du texte à afficher dans la page HTML et vous obtiendrez très certainement une erreur. La plupart des éditeurs modernes savent éditer les fichiers en UTF-8 sans BOM, notamment Notepad++. Je vous conseille l’utilisation de NetBeans pour la gestion de vos projets Web. NetBeans vous apporte en effet plein d’avantages dont la liste n’est pas l’objet de cet article. A la création d’un projet NetBeans, vous pouvez indiquer que les fichiers seront en UTF-8.

Les éditeurs fournis par défaut dans Windows, Notepad ou Wordpad, ne sont absolument pas compatibles.

Nota : sans l’aide de l’article de Nicolas Hoffmann, « Changer de jeu de caractères pour UTF-8 », j’aurais certainement mis plus de temps à trouver la solution. Merci à lui.

Franck Beulé
Chef de projet Agile, expert des technologies de l’Internet et en ergonomie du Web

Ajoutez un commentaire