UTF-8 jusqu’au bout

Je suis en train de mettre en place un nouveau serveur et je souhaite prendre pleinement en charge UTF-8 dans mon application Web. J’ai déjà essayé par le passé sur des serveurs existants et je finis toujours par avoir à revenir à la norme ISO-8859-1.

Où exactement dois-je définir l’encodage / les jeux de caractères? Je suis conscient du fait que je dois configurer Apache, MySQL et PHP pour ce faire – existe-t-il une liste de contrôle standard que je peux suivre, ou peut-être résoudre les problèmes?

Ceci est pour un nouveau serveur Linux, exécutant MySQL 5, PHP 5 et Apache 2.

Stockage de données :

  • Spécifiez le utf8mb4 caractères utf8mb4 sur toutes les tables et colonnes de texte de votre firebase database. Cela permet à MySQL de stocker et de récupérer physiquement les valeurs encodées en mode natif dans UTF-8. Notez que MySQL utilisera implicitement l’encodage utf8mb4 si un utf8mb4_* est spécifié (sans jeu de caractères explicite).

  • Dans les anciennes versions de MySQL (<5.5.3), vous serez malheureusement obligé d'utiliser simplement utf8 , qui ne supporte qu’un sous-ensemble de caractères Unicode. Je voudrais que je plaisante.

Accès aux données :

  • Dans votre code d’application (par exemple, PHP), quelle que soit la méthode d’access à la firebase database que vous utilisez, vous devez définir le charset de connexion sur utf8mb4 . De cette façon, MySQL ne fait aucune conversion de son UTF-8 natif lorsqu’il transmet des données à votre application et inversement.

  • Certains pilotes fournissent leur propre mécanisme de configuration du jeu de caractères de connexion, qui met à jour son propre état interne et informe MySQL du codage à utiliser sur la connexion. C’est généralement l’approche privilégiée. En PHP:

    • Si vous utilisez la couche d’abstraction PDO avec PHP ≥ 5.3.6, vous pouvez spécifier charset dans le DSN :

       $dbh = new PDO('mysql:charset=utf8mb4'); 
    • Si vous utilisez mysqli , vous pouvez appeler set_charset() :

       $mysqli->set_charset('utf8mb4'); // object oriented style mysqli_set_charset($link, 'utf8mb4'); // procedural style 
    • Si vous êtes bloqué avec mysql mais que vous utilisez PHP ≥ 5.2.3, vous pouvez appeler mysql_set_charset .

  • Si le pilote ne fournit pas son propre mécanisme pour définir le jeu de caractères de connexion, vous devrez peut-être lancer une requête pour indiquer à MySQL comment votre application s’attend à ce que les données de la connexion soient encodées: SET NAMES 'utf8mb4' .

  • La même considération concernant utf8mb4 / utf8 s’applique comme ci-dessus.

Sortie :

  • Si votre application transmet du texte à d’autres systèmes, ils devront également être informés du codage des caractères. Avec les applications Web, le navigateur doit être informé du codage dans lequel les données sont envoyées (via les en-têtes de réponse HTTP ou les métadonnées HTML ).

  • En PHP, vous pouvez utiliser l’option default_charset php.ini ou émettre vous-même manuellement l’en Content-Type tête MIME Content-Type , ce qui représente plus de travail mais a le même effet.

Entrée :

  • Malheureusement, vous devez vérifier chaque chaîne reçue comme étant UTF-8 valide avant d’essayer de la stocker ou de l’utiliser n’importe où. PHP mb_check_encoding() fait l’affaire, mais vous devez l’utiliser religieusement. Il n’y a vraiment aucun moyen de contourner ce problème, car les clients malveillants peuvent soumettre des données quel que soit l’encodage de leur choix, et je n’ai pas trouvé de solution pour faire en sorte que PHP le fasse de manière fiable.

  • De ma lecture de la spécification HTML actuelle, les sous-points suivants ne sont plus nécessaires ni même valables pour le HTML moderne. D’après ce que je comprends, les navigateurs utiliseront et soumettront des données dans le jeu de caractères spécifié pour le document. Cependant, si vous ciblez des versions antérieures de HTML (XHTML, HTML4, etc.), ces points peuvent toujours être utiles:

    • Pour HTML avant HTML5 uniquement : vous voulez que toutes les données envoyées par les navigateurs soient en UTF-8. Malheureusement, la seule façon de le faire est d’append l’atsortingbut accept-charset à toutes les balises

      :

      .

    • Pour HTML avant HTML5 uniquement : notez que la spécification HTML du W3C indique que les clients “devraient” par défaut renvoyer des formulaires sur le serveur quel que soit le jeu de caractères utilisé par le serveur.

Autres considérations relatives au code :

  • Evidemment, tous les fichiers que vous allez servir (PHP, HTML, JavaScript, etc.) doivent être encodés en UTF-8 valide.

  • Vous devez vous assurer que chaque fois que vous traitez une chaîne UTF-8, vous le faites de manière sécurisée. C’est malheureusement la partie la plus difficile. Vous voudrez probablement utiliser largement l’extension mbssortingng de PHP.

  • Les opérations de chaîne intégrées de PHP ne sont pas sécurisées par défaut pour UTF-8. Il y a certaines choses que vous pouvez faire en toute sécurité avec les opérations sur les chaînes PHP normales (comme la concaténation), mais pour la plupart des choses, vous devez utiliser la fonction mbssortingng équivalente.

  • Pour savoir ce que vous faites (lisez: ne pas le gâcher), vous devez vraiment connaître UTF-8 et son fonctionnement au niveau le plus bas possible. Consultez l’un des liens sur utf8.com pour trouver de bonnes ressources pour apprendre tout ce que vous devez savoir.

J’aimerais append une chose à l’excellente réponse de chazomaticus :

N’oubliez pas non plus la balise META (comme celle-ci, ou la version HTML4 ou XHTML ):

  

Cela semble sortingvial, mais IE7 m’a déjà posé des problèmes.

Je faisais tout bien; la firebase database, la connexion à la firebase database et l’en-tête HTTP Content-Type étaient tous définis sur UTF-8, et cela fonctionnait bien dans tous les autres navigateurs, mais Internet Explorer insistait toujours pour utiliser le codage “Europe de l’Ouest”.

La balise META manquait. Ajout qui a résolu le problème.

Modifier:

Le W3C a en fait une section assez grande dédiée à I18N . Ils ont un certain nombre d’articles liés à ce problème – décrivant le côté HTTP, (X) HTML et CSS:

  • FAQ: Modification du codage de page HTML (X) en UTF-8
  • Déclaration des encodages de caractères en HTML
  • Tutoriel: Jeux de caractères et encodages en XHTML, HTML et CSS
  • Définition du paramètre HTTP charset

Ils recommandent d’utiliser à la fois l’en-tête HTTP et la balise meta HTML (ou la déclaration XML dans le cas de XHTML servi en XML).

En plus de définir default_charset dans php.ini, vous pouvez envoyer le bon jeu de caractères en utilisant header() depuis votre code, avant toute sortie:

 header('Content-Type: text/html; charset=utf-8'); 

Travailler avec Unicode en PHP est facile tant que vous vous rendez compte que la plupart des fonctions de chaîne ne fonctionnent pas avec Unicode, et que certaines peuvent modifier complètement les chaînes . PHP considère les “caractères” comme longs de 1 octet. Parfois, c’est correct (par exemple, explode() ne recherche qu’une séquence d’octets et l’utilise comme séparateur – peu importe les caractères recherchés). Mais d’autres fois, lorsque la fonction est conçue pour fonctionner sur des caractères , PHP ne sait pas que votre texte contient des caractères multi-octets trouvés avec Unicode.

Une bonne bibliothèque à vérifier est phputf8 . Cela réécrit toutes les “mauvaises” fonctions afin que vous puissiez travailler en toute sécurité sur les chaînes UTF8. Il y a des extensions comme l’extension mbssortingng qui essaient de le faire pour vous aussi, mais je préfère utiliser la bibliothèque car elle est plus portable (mais j’écris des produits grand public, donc c’est important pour moi). Mais de toute façon, phputf8 peut utiliser mbssortingng en coulisse pour améliorer les performances.

Vieux sujet, je sais. Vous avez rencontré un problème avec une personne utilisant PDO et la réponse était de l’utiliser pour la chaîne de connexion PDO:

 $pdo = new PDO( 'mysql:host=mysql.example.com;dbname=example_db', "username", "password", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")); 

Le site que j’ai pris à partir de là est en panne, a pu l’obtenir en utilisant heureusement le cache de Google.

Dans mon cas, j’utilisais mb_split , qui utilise regex. Par conséquent, j’ai également dû m’assurer manuellement que l’encodage regex était utf-8 en mb_regex_encoding('UTF-8');

En guise de remarque, j’ai également découvert en exécutant mb_internal_encoding() que le codage interne n’était pas utf-8, et j’ai changé cela en exécutant mb_internal_encoding("UTF-8"); .

Tout d’abord si vous êtes dans <5.3PHP alors non. Vous avez beaucoup de problèmes à résoudre.

Je suis surpris que personne n’ait mentionné la bibliothèque intl , celle qui prend bien en charge l’ unicode , les graphèmes , les opérations sur les chaînes , la localisation et bien d’autres, voir ci-dessous.

Je citerai quelques informations sur la prise en charge d’Unicode dans PHP par les diapositives d’ Elizabeth Smith à PHPBenelux ’14

INTL

Bien:

  • Wrapper autour de la bibliothèque de l’ICU
  • Paramètres régionaux standardisés, définissez les parameters régionaux par script
  • Formatage des nombres
  • Mise en forme de la devise
  • Formatage du message (remplace gettext)
  • Calendriers, dates, fuseau horaire et heure
  • Translittérateur
  • Spoofchecker
  • Regroupements de ressources
  • Convertisseurs
  • Support IDN
  • Graphemes
  • Collation
  • Les iterators

Mal:

  • Ne supporte pas zend_multibite
  • Ne prend pas en charge la conversion de sortie d’entrée HTTP
  • Ne supporte pas la surcharge de fonctions

mb_ssortingng

  • Active le support zend_multibyte
  • Prend en charge l’encodage HTTP in / out transparent
  • Fournit des wrappers pour la fonctionnalité comme strtoupper

ICONV

  • Primaire pour la conversion du jeu de caractères
  • Gestionnaire de tampon de sortie
  • fonctionnalité d’encodage mime
  • conversion
  • des aides de chaîne (len, substr, strpos, strrpos)
  • Filtre de stream stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

BASES DE DONNÉES

  • mysql: Jeu de caractères et classement sur les tables et sur la connexion (pas le classement). N’utilisez pas non plus mysql – msqli ou PDO
  • postgresql: pg_set_client_encoding
  • sqlite (3): Assurez-vous qu’il a été compilé avec le support Unicode et Intl

Quelques autres Gotchas

  • Vous ne pouvez pas utiliser les noms de fichiers unicode avec PHP et Windows, sauf si vous utilisez une extension de troisième partie.
  • Envoyez tout en ASCII si vous utilisez exec, proc_open et d’autres appels en ligne de commande
  • Le texte brut n’est pas du texte brut, les fichiers ont des encodages
  • Vous pouvez convertir des fichiers à la volée avec le filtre iconv

Je mettrai à jour cette réponse au cas où des modifications seraient apscopes aux fonctionnalités, etc.

J’ai récemment découvert que l’utilisation de strtolower() peut causer des problèmes lorsque les données sont tronquées après un caractère spécial.

La solution était d’utiliser

 mb_strtolower($ssortingng, 'UTF-8'); 

mb_ utilise MultiByte. Il supporte plus de personnages mais est en général un peu plus lent.

La seule chose que je voudrais append à ces réponses étonnantes est de mettre l’accent sur la sauvegarde de vos fichiers dans l’encodage utf8, j’ai remarqué que les navigateurs acceptent cette propriété plutôt que de définir utf8 comme code d’encodage. Tout éditeur de texte décent vous le montrera, par exemple Notepad ++ a une option de menu pour l’encodage des fichiers, il vous montre l’encodage actuel et vous permet de le modifier. Pour tous mes fichiers php, j’utilise utf8 sans nomenclature.

Il y a quelque temps, quelqu’un m’a demandé d’append le support utf8 pour une application php / mysql conçue par quelqu’un d’autre, j’ai remarqué que tous les fichiers étaient encodés en ANSI, donc j’ai dû utiliser ICONV pour convertir tous les fichiers. utf8 charset et utf8_general_ci assemblent, ajoutent ‘SET NAMES utf8’ à la couche d’abstraction de la firebase database après la connexion (si 5.3.6 ou une version antérieure est utilisée, sinon vous devez utiliser charset = utf8 dans la chaîne de connexion) et modifiez les fonctions de chaîne fonctions de chaîne équivalentes.

En PHP, vous devrez soit utiliser les fonctions multi – octets , soit activer mbssortingng.func_overload . De cette façon, des choses comme strlen fonctionneront si vous avez des caractères qui prennent plus d’un octet.

Vous devrez également identifier le jeu de caractères de vos réponses. Vous pouvez soit utiliser AddDefaultCharset, comme ci-dessus, soit écrire du code PHP qui renvoie l’en-tête. (Ou vous pouvez append une balise META à vos documents HTML.)

Je viens de parcourir le même problème et j’ai trouvé une bonne solution dans les manuels PHP.

J’ai changé tout mon fichier encodé en UTF8 puis l’encodage par défaut sur ma connexion. Cela a résolu tous les problèmes.

 if (!$mysqli->set_charset("utf8")) { printf("Error loading character set utf8: %s\n", $mysqli->error); } else { printf("Current character set: %s\n", $mysqli->character_set_name()); } 

Voir la source

Bon objective à atteindre dès le départ – basé sur la nature de votre site, j’ai trouvé beaucoup de ressources à ce sujet sur Google – vous n’êtes bien sûr pas les premiers à y faire face.

Le PHP6 mystique est supposé avoir tout cela corrigé, non?

Vous pouvez à peu près définir utf-8 comme jeu de caractères global par défaut pour mysql au niveau du serveur et il utilisera par défaut les niveaux les plus précis.

La prise en charge d’Unicode dans PHP est encore un énorme gâchis. Bien qu’il soit capable de convertir une chaîne ISO8859 (qu’il utilise en interne) en utf8, il n’a pas la capacité de fonctionner en mode natif avec les chaînes Unicode, ce qui signifie que toutes les fonctions de traitement de chaînes modifient et corrompent vos chaînes. Donc, vous devez soit utiliser une bibliothèque distincte pour le support utf8 approprié, soit réécrire vous-même toutes les fonctions de gestion de chaîne.

La partie facile consiste simplement à spécifier le jeu de caractères dans les en-têtes HTTP et dans la firebase database, mais rien de tout cela n’est important si votre code PHP n’a pas généré d’UTF8 valide. C’est la partie la plus difficile, et PHP ne vous aide pratiquement pas là-bas. (Je pense que PHP6 est censé réparer le pire, mais il rest encore un certain temps)

La meilleure réponse est excellente. Voici ce que je devais faire sur une installation régulière de debian / php / mysql:

 // storage // debian. apparently already utf-8 // resortingeval // the mysql database was stored in utf-8, // but apparently php was requesting iso. this worked: // ***notice "utf8", without dash, this is a mysql encoding*** mysql_set_charset('utf8'); // delivery // php.ini did not have a default charset, // (it was commented out, shared host) and // no http encoding was specified in the apache headers. // this made apache send out a utf-8 header // (and perhaps made php actually send out utf-8) // ***notice "utf-8", with dash, this is a php encoding*** ini_set('default_charset','utf-8'); // submission // this worked in all major browsers once apache // was sending out the utf-8 header. i didnt add // the accept-charset atsortingbute. // processing // changed a few commands in php, like substr, // to mb_substr 

c’était tout !

Si vous voulez que le serveur MySQL décide du jeu de caractères, et non de PHP en tant que client (ancien comportement, à mon avis préféré), essayez d’append skip-character-set-client-handshake à my.cnf , sous [mysqld] , et redémarrez mysql .

Cela peut causer des problèmes si vous utilisez autre chose que UTF8.