UTF-8 tout au long

1191

J'installe un nouveau serveur et souhaite prendre en charge UTF-8 entièrement dans mon application Web. J'ai essayé cela dans le passé sur des serveurs existants et il me semble toujours devoir revenir à ISO-8859-1.

Où dois-je exactement définir le codage / les jeux de caractères? Je suis conscient que je dois configurer Apache, MySQL et PHP pour ce faire - y a-t-il une liste de contrôle standard que je peux suivre, ou peut-être dépanner où les disparités se produisent?

Il s'agit d'un nouveau serveur Linux, exécutant MySQL 5, PHP, 5 et Apache 2.

mercutio
la source
8
Voici un aperçu de toutes les erreurs de codage que vous pouvez éventuellement commettre: sebastianviereck.de/en/…
Sebastian Viereck
13
Voici une introduction aux encodages en général et aux encodages en PHP en particulier: Ce que chaque programmeur doit absolument savoir de manière positive sur les encodages et les jeux de caractères pour travailler avec du texte
deceze
Certaines discussions récentes sur PHP 7 indiquent qu'il n'y a pas de changement dans la position "officiellement abandonnée" de 2010 ... Il y a quelque chose de plus sur "PHP7 et UTF-8"?
Peter Krauss
Ce problème est courant. Mais il n'y a pas de solution de raccourci, vous devrez configurer utf-8séparément pour chacun d'eux - MySQL 5, PHP 5 OU Apache 2.
Manish Shrivastava

Réponses:

1016

Stockage de données :

  • Spécifiez le utf8mb4jeu de caractères sur toutes les tables et colonnes de texte de votre base de données. Cela fait que MySQL stocke et récupère physiquement les valeurs encodées nativement en UTF-8. Notez que MySQL utilisera implicitement l' utf8mb4encodage si un utf8mb4_*classement est spécifié (sans aucun 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 souhaite que je plaisante.

Accès aux données :

  • Dans votre code d'application (par exemple PHP), quelle que soit la méthode d'accès à la base de données que vous utilisez, vous devrez définir le jeu de caractères de connexion sur utf8mb4. De cette façon, MySQL n'effectue aucune conversion à partir de son UTF-8 natif lorsqu'il transfère des données à votre application et vice versa.

  • Certains pilotes fournissent leur propre mécanisme de configuration du jeu de caractères de connexion, qui à la fois met à jour son propre état interne et informe MySQL du codage à utiliser sur la connexion - c'est généralement 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 charsetdans 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 ordinaire mais que vous utilisez PHP ≥ 5.2.3, vous pouvez appeler mysql_set_charset.

  • Si le conducteur ne fournit pas son propre mécanisme de fixation du jeu de caractères de connexion, vous pouvez avoir à émettre une requête pour indiquer à MySQL comment votre application attend des données sur la connexion à coder: SET NAMES 'utf8mb4'.

  • La même considération concernant utf8mb4/ utf8s'applique que 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é de l'encodage 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' default_charsetoption php.ini ou émettre manuellement l' Content-Typeen-tête MIME vous-même, ce qui est juste plus de travail mais a le même effet.

  • Lors de l'encodage de la sortie à l'aide de json_encode(), ajoutez JSON_UNESCAPED_UNICODEcomme deuxième paramètre.

Entrée :

  • Malheureusement, vous devez vérifier que chaque chaîne reçue est 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 cela, car les clients malveillants peuvent soumettre des données dans l'encodage de leur choix, et je n'ai trouvé aucune astuce pour que PHP le fasse pour vous de manière fiable.

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

    • Pour HTML avant HTML5 uniquement : vous voulez que toutes les données qui vous sont envoyées par les navigateurs soient en UTF-8. Malheureusement, si vous passez par la seule façon de le faire est d' ajouter de manière fiable ce l' accept-charsetattribut à tous vos <form>tags: <form ... accept-charset="UTF-8">.
    • Pour HTML avant HTML5 uniquement : notez que la spécification HTML du W3C dit que les clients "devraient" par défaut renvoyer des formulaires au serveur dans le jeu de caractères que le serveur a servi, mais cela n'est apparemment qu'une recommandation, d'où la nécessité d'être explicite sur chaque élément. <form>étiquette.

Autres considérations relatives au code :

  • De toute évidence, 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' mbstringextension PHP .

  • Les opérations de chaîne intégrées de PHP ne sont pas par défaut sécurisées UTF-8. Il y a certaines choses que vous pouvez 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 mbstringfonction équivalente .

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

chazomaticus
la source
4
Je crois comprendre que si vous spécifiez le classement comme utf8_ *, il code également automatiquement en utf8. Est-ce mal?
chazomaticus
49
Je ne me trompe pas: COLLATE implique CHARACTER SET. Voir par exemple dev.mysql.com/doc/refman/5.0/en/charset-database.html .
chazomaticus
7
Pensez également à ajouter des exemples PDO pour définir le jeu de caractères.
Ja͢ck
97
Notez que MySQL ne parle pas le même langage que tout le monde. Quand MySQL dit "utf8" cela signifie vraiment "une variante étrangement retardée de l'UTF-8 qui est limitée à trois octets car Dieu sait quelle raison ridicule". Si vous voulez vraiment UTF-8, vous devez dire à MySQL que vous voulez cette chose étrange que MySQL aime appeler utf8mb4 . Ne vous embêtez pas à économiser sur les "WTF!".
R. Martinho Fernandes
4
Cette réponse m'a tellement aidé MAIS j'ai également trouvé que dans mon cas, j'avais besoin d'ajouter JSON_UNESCAPED_UNICODE à mon PHP json_encode lors du renvoi des résultats de la requête DB via ajax.
Petay87
150

Je voudrais ajouter une chose à l'excellente réponse de chazomaticus :

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

<meta charset="utf-8">

Cela semble anodin, mais IE7 m'a déjà posé des problèmes avec cela.

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 définis sur UTF-8, et cela fonctionnait bien dans tous les autres navigateurs, mais Internet Explorer insistait toujours pour utiliser l'encodage "Europe occidentale".

Il s'est avéré que la page n'avait pas la balise META. L'ajout a résolu le problème.

Éditer:

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

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

mercator
la source
Ne devrait-il pas également être possible de spécifier le jeu de caractères dans les en-têtes HTTP? A probablement besoin d'une option de configuration pour le serveur Web ...
Oliver
2
@oliver: Oui, vous pouvez l'envoyer dans l'en-tête HTTP, mais il est préférable de l'envoyer dans le contenu car si le client enregistre le fichier, il enregistrera toujours la balise META. Un en-tête HTTP est susceptible de disparaître à moins que le navigateur soit suffisamment intelligent pour le copier dans une balise META dans le fichier enregistré.
5
Assurez-vous également que cette ligne est le premier enfant de l'élément head (avant tout élément Unicode). Le navigateur peut réinterpréter la page après avoir atteint le méta-élément décrit ci-dessus.
alex
64

En plus de la configuration default_charsetdans php.ini, vous pouvez envoyer le jeu de caractères correct à l'aide header()de votre code, avant toute sortie:

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

Travailler avec Unicode en PHP est facile tant que vous réalisez que la plupart des fonctions de chaîne ne fonctionnent pas avec Unicode, et certaines peuvent complètement supprimer les chaînes . PHP considère que les "caractères" font 1 octet de long. Parfois, cela explode()ne pose aucun problème (par exemple, ne recherche qu'une séquence d'octets et l'utilise comme séparateur - donc peu importe les caractères réels que vous recherchez). Mais d'autres fois, lorsque la fonction est réellement conçue pour fonctionner sur des caractères , PHP n'a aucune idée que votre texte contient des caractères multi-octets trouvés avec Unicode.

Phputf8 est une bonne bibliothèque à vérifier . 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 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 en coulisses, de toute façon, pour augmenter les performances.

chroder
la source
Définissez le paramètre de surcharge dans php.ini. Cela aide lors de l'utilisation de chaînes multi-octets.
Anthony Rutledge
32

J'ai trouvé un problème avec quelqu'un utilisant PDO et la réponse a été d'utiliser ceci 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 ceci est en panne, mais j'ai pu l'obtenir en utilisant le cache Google, heureusement.

Jim W.
la source
1
En cherchant un peu plus loin, cela n'est nécessaire que pour les versions PHP antérieures à 5.3.6. Voir aussi: http://stackoverflow.com/a/4361485/2286722 (bien qu'ils utilisent un séparé $dbh->exec("set names utf8");; je préfère la méthode présentée ici). Btw. il y a aussi une note similaire à ce sujet en tant que commentaire dans le manuel PHP: php.net/manual/en/pdo.construct.php#96325 .
Marten Koetsier
24

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

En remarque, j'ai également 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");.

JDelage
la source
22

Tout d'abord si vous êtes <5.3PHP alors non. Vous avez une tonne 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 de chaîne , la localisation et bien d'autres, voir ci-dessous.

Je vais citer quelques informations sur la prise en charge unicode en PHP par les diapositives d' Elizabeth Smith sur PHPBenelux'14

INTL

Bien:

  • Wrapper autour de la bibliothèque ICU
  • Paramètres régionaux standardisés, définir les paramètres régionaux par script
  • Formatage des nombres
  • Formatage des devises
  • Formatage des messages (remplace gettext)
  • Calendriers, dates, fuseau horaire et heure
  • Translittérateur
  • Spoofchecker
  • Regroupements de ressources
  • Convertisseurs
  • Prise en charge IDN
  • Graphèmes
  • Collation
  • Itérateurs

Mauvais:

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

mb_string

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

ICONV

  • Principal 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: Charset 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 de noms de fichiers unicode avec PHP et Windows à moins d'utiliser une extension de 3e partie.
  • Envoyez tout en 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 ont des encodages
  • Vous pouvez convertir des fichiers à la volée avec le filtre iconv

Je mettrai à jour cette réponse au cas où les choses changeraient, les fonctionnalités ajoutées, etc.

Jimmy Kane
la source
2
Oui bien. Mysqli et PDO peuvent utiliser leurs pilotes natifs. Ils peuvent également utiliser le pilote mysqlnd si vous compilez php avec des --with-mysqli=mysqlnd --with-pdo-mysql=mysqlndoptions.
Alexander Yancharuk
14

La seule chose que j'ajouterais à ces réponses incroyables est de mettre l'accent sur l'enregistrement 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 encodage de code. Tout éditeur de texte décent vous le montrera, par exemple Notepad ++ a une option de menu pour la liaison de fichiers, il vous montre l'encodage actuel et vous permet de le changer. Pour tous mes fichiers php, j'utilise utf8 sans BOM.

Il y a quelque temps, 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, j'ai donc dû utiliser ICONV pour convertir tous les fichiers, changer les tables de base de données pour utiliser le utf8 charset et utf8_general_ci collate, 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 sinon vous devez utiliser charset = utf8 dans la chaîne de connexion) et changer les fonctions de chaîne pour utiliser le multi-octet php fonctions de chaîne équivalentes.

Puerto AGP
la source
13

J'ai récemment découvert qu'en utilisant strtolower() peut provoquer des problèmes où les données sont tronquées après un caractère spécial.

La solution consistait à utiliser

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

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

Miguel Stevens
la source
9

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

J'ai changé tout mon encodage de fichier 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

Abdul Sadik Yalcin
la source
2
J'ai passé une heure à essayer de résoudre un problème d'encodage sur une page sur laquelle je travaille et je suis généralement assez bon pour comprendre les choses. Je consulte toujours cette page et votre réponse m'a beaucoup aidé. J'ai mon vote positif. Dans mon cas, cela set_charset('utf8mb4')n'a pas fonctionné mais >set_charset("utf8")a fonctionné et cela n'a pas été montré dans les autres réponses.
Funk Forty Niner
@FunkFortyNiner Attention: set_charset("utf8")peut fonctionner mais se comportera différemment (voir les remarques sur la différence entre utf8et utf8mb4et l'historique des versions de mysql). À utiliser utf8 si vous devez ET UNIQUEMENT si vous savez ce que vous faites !
Martin Hennings
Solution 5 étoiles, je lisais un fichier texte ligne par ligne et j'obtenais? pour chaque personnage, j'ai ensuite sauvegardé car, au lieu de ansi, j'ai utilisé utf8. Merci.
Atef Farouk
8

En PHP, vous devrez soit utiliser les 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, soit écrire du code PHP qui renvoie l'en-tête. (Ou vous pouvez ajouter une balise META à vos documents HTML.)

JW.
la source
Excellente astuce sur le paramètre func_overload - permet une modification minimale du code existant.
Simon East
4
Faites juste attention - certains codes peuvent en fait s'appuyer sur la nature d'un octet par caractère des fonctions de chaîne standard.
JW.
Il est important de noter que la fonctionnalité mbstring.func_overload est obsolète à partir de PHP 7.2, en raison des problèmes notés dans le commentaire de @ JW ci-dessus. Le meilleur conseil est donc: Oui, vous devez absolument utiliser les fonctions mbstring, mais n'utilisez pas la fonction de surcharge pour que les fonctions standard fonctionnent en multioctets.
Simba
6

Le support Unicode en PHP est toujours 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 des chaînes unicode, ce qui signifie que toutes les fonctions de traitement de chaîne vont altérer et corrompre vos chaînes. Vous devez donc soit utiliser une bibliothèque distincte pour une prise en charge appropriée de utf8, soit réécrire toutes les fonctions de gestion des chaînes vous-même.

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, etc. C'est la partie difficile, et PHP ne vous y aide pratiquement pas. (Je pense que PHP6 est censé résoudre le pire de tout cela, mais c'est encore un peu plus loin)

jalf
la source
6

Si vous souhaitez que le serveur MySQL décide du jeu de caractères, et non 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 entraîner des problèmes si vous utilisez autre chose que UTF8.

Budimir Grom
la source
5

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

// 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 !

pic commun
la source
1

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 bien!

J'ai trouvé cette solution https://www.w3schools.com/PHP/func_mysqli_set_charset.asp lorsque je cherchais à résoudre un insert à partir d'une requête html

bonne chance!

castro_pereira
la source
1

Juste une note:

Vous êtes face au problème de vos personnages non-latin est montrant que ?????????, vous avez posé une question, et il a été fermé avec une référence à cette question canonique, vous avez tout essayé et peu importe ce que vous faites vous obtenez toujours ??????????deMySQL .

C'est principalement parce que vous testez vos anciennes données qui ont été insérées dans la base de données en utilisant le mauvais jeu de caractères et qui ont été converties et stockées en fait les caractères du point d'interrogation ?. Ce qui signifie que vous avez perdu votre texte d'origine pour toujours et peu importe ce que vous essayez, vous obtiendrez??????? .

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

Comptable م
la source
0

J'ai eu ce problème lors de l'affichage des tableaux. Je viens de mettre cela sur chaque variable de sortie d'écho:

<td><?php echo utf8_encode ($Local) ?></td>
Joao Fonseca
la source