web-dev-qa-db-fra.com

UTF-8 tout au long de

Je configure un nouveau serveur et souhaite prendre en charge UTF-8 dans mon application Web. J'ai déjà essayé cela sur des serveurs existants et semble toujours devoir me rabattre sur ISO-8859-1.

Où dois-je exactement définir l'encodage/les jeux de caractères? Je suis conscient de la nécessité de configurer Apache, MySQL et PHP pour ce faire. Existe-t-il une liste de contrôle standard que je peux suivre, ou peut-être que l'on puisse résoudre le problème d'incompatibilités?

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

1133
mercutio

Stockage de données :

  • Spécifiez le jeu de caractères _utf8mb4_ sur toutes les tables et toutes les colonnes de texte de votre base de données. Cela permet à MySQL de stocker et de récupérer physiquement les valeurs encodées de manière native en UTF-8. Notez que MySQL utilisera implicitement le codage _utf8mb4_ si un classement _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 prend en charge qu'un sous-ensemble de caractères Unicode. Je voudrais plaisanter.

Accès aux données :

  • Dans votre code d'application (PHP, par exemple), quelle que soit la méthode d'accès à la base de données utilisée, vous devez définir le jeu de caractères de connexion sur _utf8mb4_. De cette façon, MySQL n'effectue 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 (il s'agit généralement de l'approche préférée. En PHP:

    • Si vous utilisez la couche d'abstraction PDO avec PHP ≥ 5.3.6, vous pouvez spécifier charset dans 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 coincé avec plain 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 la 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 codées: SET NAMES 'utf8mb4' .

  • Les mêmes considérations concernant _utf8mb4_/_utf8_ s'appliquent comme ci-dessus.

Sortie :

  • Si votre application transmet du texte à d'autres systèmes, ceux-ci 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 métadonnées HTML ).

  • En PHP, vous pouvez utiliser l'option default_charset php.ini ou attribuer manuellement l'en-tête _Content-Type_ MIME vous-même, ce qui représente davantage de travail mais a le même effet.

  • Lors du codage de la sortie à l'aide de json_encode(), ajoutez _JSON_UNESCAPED_UNICODE_ en tant que deuxième paramètre.

Entrée :

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

  • D'après ma lecture du HTML spec actuel, les sous-puces suivantes ne sont plus nécessaires ni même plus valables pour le HTML moderne. D'après ce que j'ai compris, les navigateurs travailleront avec les données et les soumettront dans le jeu de caractères spécifié pour le document. Cependant, si vous ciblez d'anciennes versions de HTML (XHTML, HTML4, etc.), ces points peuvent toujours être utiles:

    • Pour HTML avant HTML5 uniquement : vous souhaitez que toutes les données envoyées par les navigateurs soient en UTF-8. Malheureusement, si vous optez pour le seul moyen fiable de procéder, ajoutez l’attribut _accept-charset_ à toutes vos balises _<form>_: _<form ... accept-charset="UTF-8">_.
    • Pour HTML antérieur à HTML5 uniquement : notez que la spécification HTML du W3C indique que les clients "devraient" par défaut pour renvoyer les formulaires au serveur, quel que soit le jeu de caractères que le serveur a servi, mais ce n'est apparemment une recommandation, d'où la nécessité d'être explicite sur chaque balise _<form>_.

Autres considérations sur le code :

  • Bien évidemment, 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 en toute sécurité. C'est malheureusement la partie la plus difficile. Vous voudrez probablement faire un usage intensif de l'extension mbstring de PHP.

  • Les opérations de chaîne intégrées à PHP sont et non par défaut avec la sécurité UTF-8. peut le faire en toute sécurité avec les opérations de chaîne PHP normales (comme la concaténation), mais pour la plupart des choses, vous devez utiliser la fonction équivalente mbstring.

  • Pour savoir ce que vous faites (lisez: ne le gâchez pas), vous devez vraiment connaître le format UTF-8 et son fonctionnement au niveau le plus bas possible. Découvrez l'un des liens de tf8.com pour trouver de bonnes ressources pour apprendre tout ce que vous devez savoir.

972
chazomaticus

J'aimerais ajouter une chose à excellente réponse de chazomaticus :

N'oubliez pas la balise META soit (comme ceci, ou la version HTML4 ou XHTML de celle-ci ):

<meta charset="utf-8">

Cela semble anodin, mais IE7 m’a déjà posé problème auparavant.

Je faisais tout bien. la base de données, la connexion à la base de données et l'en-tête HTTP Content-Type étaient tous réglés sur UTF-8 et fonctionnaient correctement sur tous les autres navigateurs, mais Internet Explorer insistait toujours pour utiliser le codage "Europe occidentale".

Il s'est avéré que la page manquait l'étiquette META. Ajouter cela a résolu le problème.

Modifier:

Le W3C a en fait un assez grand section dédiée à I18N . Ils ont plusieurs articles sur ce problème, décrivant les aspects HTTP, (X) HTML et CSS:

Ils recommandent d'utiliser à la fois l'en-tête HTTP et la balise méta HTML (ou la déclaration XML dans le cas où XHTML est utilisé en tant que XML).

146
mercator

En plus de définir default_charset dans php.ini, vous pouvez envoyer le jeu de caractères correct à l'aide de header() à partir de votre code, avant toute sortie:

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

Travailler avec Unicode dans PHP est simple, 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 que les "caractères" ont une longueur de 1 octet. Parfois, c'est correct (par exemple, explode() ne recherche qu'une séquence d'octets et l'utilise comme séparateur - le type de caractère recherché n'a donc pas d'importance.). Mais d'autres fois, lorsque la fonction est conçue pour fonctionner sur caractères , PHP n'a aucune idée que votre texte comporte des caractères multi-octets trouvés avec Unicode. .

Une bonne bibliothèque à vérifier est phputf8 . Ceci réécrit toutes les "mauvaises" fonctions afin que vous puissiez travailler en toute sécurité sur les chaînes UTF8. Il existe des extensions telles que l'extension mbstring 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 phputf8 peut utiliser mbstring dans les coulisses pour augmenter ses performances.

61
chroder

J'ai trouvé un problème avec une personne utilisant PDO et la réponse a été 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 sur lequel j'ai pris cette information est en panne, mais heureusement, j'ai pu l'obtenir à l'aide du cache de Google.

28
Brad F Jacobs

Dans mon cas, j'utilisais mb_split, qui utilise regex. Par conséquent, je devais aussi m'assurer manuellement que le codage regex était utf-8 en faisant mb_regex_encoding('UTF-8');

En remarque, j'ai aussi découvert en exécutant mb_internal_encoding() que l'encodage interne n'était pas utf-8, et j'ai changé cela en exécutant mb_internal_encoding("UTF-8");.

22
JDelage

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

Je suis surpris qu’aucune d’entre elles n’ait mentionné la bibliothèque intl , celle qui supporte bien unicode , graphèmes , opérations sur les chaînes , localisation et beaucoup d'autres, voir ci-dessous.

Je citerai quelques informations sur le support unicode dans PHP de par Elizabeth Smith slides à PHPBenelux'14

INTL

Bien:

  • Wrapper autour de la bibliothèque ICU
  • Paramètres régionaux normalisés, définir les paramètres régionaux par script
  • Mise en forme numérique
  • Format de devise
  • Formatage des messages (remplace gettext)
  • Calendriers, dates, fuseau horaire et heure
  • Traducteur
  • Spoofchecker
  • Kits de ressources
  • Convertisseurs
  • Support IDN
  • Graphèmes
  • Collation
  • Itérateurs

Mauvais:

  • Ne supporte pas zend_multibite
  • Ne supporte pas la conversion d'entrée/sortie HTTP
  • Ne supporte pas la surcharge de fonction

mb_string

  • Active le support zend_multibyte
  • Prend en charge le codage HTTP in/out transparent
  • Fournit des wrappers pour des fonctionnalités telles que strtoupper

ICONV

  • Primaire pour la conversion du jeu de caractères
  • Gestionnaire de tampon de sortie
  • fonctionnalité d'encodage mime
  • conversion
  • quelques aides de chaîne (len, substr, strpos, strrpos)
  • Filtre de flux 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). Aussi, n'utilisez pas 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 pièges

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

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

21
Jimmy Kane

J'ai récemment découvert que l'utilisation de strtolower() peut entraîner des problèmes de données tronquées après un caractère spécial.

La solution était d'utiliser

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

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

13
Notflip

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

Quelque temps auparavant, quelqu'un m'a demandé d'ajouter 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. Je devais donc utiliser ICONV pour convertir tous les fichiers, modifier les tables de la base de données utf8 charset et utf8_general_ci s'assemblent, ajoutez 'SET NAMES utf8' à la couche d'abstraction de la base de données après la connexion (si vous utilisez 5.3.6 ou une version antérieure, vous devez utiliser charset = utf8 dans la chaîne de connexion) et modifiez les fonctions de chaîne pour utiliser le multibyte php. fonctions de chaîne équivalentes.

12
Puerto AGP

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

J'ai changé tout mon encodage de fichier en UTF8 puis l'encodage par défaut de 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

8

En PHP, vous devez soit utiliser fonctions multi-octets , soit activer mbstring.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, ou écrire PHP code qui retourne l'en-tête. (Vous pouvez également ajouter une balise META à vos documents HTML.)

8
JW.

Le support Unicode dans PHP est toujours un désastre énorme. 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 travailler avec des chaînes unicode de manière native, ce qui signifie que toutes les fonctions de traitement de chaîne modifieront et corrompront vos chaînes. Vous devez donc utiliser une bibliothèque distincte pour la prise en charge appropriée d'utf8 ou réécrire vous-même toutes les fonctions de gestion des chaînes.

La partie facile consiste simplement à spécifier le jeu de caractères dans les en-têtes HTTP et dans la base de données, mais cela n’a aucune importance si votre code PHP ne génère pas de code UTF8 valide. C'est la partie difficile, et PHP ne vous aide pratiquement pas. (Je pense que PHP6 est supposé résoudre le pire, mais c'est encore loin)

6
jalf

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

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

5
Nikola Tulimirovic

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

// storage
// debian. apparently already utf-8

// retrieval
// 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 attribute.

// processing
// changed a few commands in php, like substr,
// to mb_substr

c'était tout !

4
commonpike

Juste une note:

Vous êtes confronté au problème de vos caractères non-latins qui se présentent comme ?????????, vous avez posé une question, et il s'est fermé avec une référence à cette question canonique, vous avez tout essayé et peu importe ce que vous obtenez, vous obtenez toujours ?????????? de MySQL.

C’est principalement parce que vous testez vos anciennes données qui a été inséré dans la base de données à l’aide du mauvais jeu de caractères, puis converti et stocké sous les caractères de point d’interrogation ?. Ce qui signifie que vous avez perdu votre texte original pour toujours et peu importe ce que vous essayez, vous obtiendrez ???????.

en appliquant ce que vous avez appris des réponses de cette question sur de nouvelles données, cela pourrait résoudre votre problème.

0
Accountant م

si vous voulez une solution mysql, j'ai eu des problèmes similaires avec 2 de mes projets, après une migration de serveur. Après avoir cherché et essayé beaucoup de solutions, je suis tombé sur celui-ci/rien avant que celui-ci ne fonctionne):

mysqli_set_charset($con,"utf8");

Après avoir ajouté cette ligne à mon fichier de configuration, tout fonctionne correctement!

J'ai trouvé cette solution https://www.w3schools.com/PHP/func_mysqli_set_charset.asp alors que je cherchais à résoudre une requête d'insertion à partir de HTML

bonne chance!

0
castro_pereira