54 votes

PHP DomDocument défaut pour gérer les caractères utf-8 (☆)

Le serveur web est de servir les réponses avec le codage utf-8, tous les fichiers sont enregistrés avec l'encodage utf-8, et tout ce que je sais de paramètre a été défini pour l'encodage utf-8.

Voici un petit programme pour tester si la sortie fonctionne:

<?php
$html = <<<HTML
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Test!</title>
</head>
<body>
    <h1>☆ Hello ☆ World ☆</h1>
</body>
</html>
HTML;

$dom = new DomDocument("1.0", "utf-8");
$dom->loadHTML($html);

header("Content-Type: text/html; charset=utf-8");
echo($dom->saveHTML());

La sortie du programme est:

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Test!</title></head><body>
    <h1>&acirc;&#152;&#134; Hello &acirc;&#152;&#134; World &acirc;&#152;&#134;</h1>
</body></html>

Qui se traduit par:

↠Bonjour ↠Monde â†


Que pouvais-je fait de mal? Comment beaucoup plus spécifique dois-je dire à la DomDocument pour gérer correctement l'utf-8?

116voto

hakre Points 102271

Comme dit dans le chat, DOMDocument::loadHTML() s'attend à une chaîne HTML.

HTML utilise l' ISO-8859-1 de l'encodage (ISO Alphabet Latin n ° 1) en tant que par défaut c'est les specs. C'est depuis plus longtemps, voir 6.1. Le Jeu de Caractères de Document. En réalité, c'est plus de la prise en charge par défaut pour Windows-1252 dans la commune de navigateurs internet.

Je reviens de loin car PHP est DOMDocument est basé sur libxml et qui apporte la HTMLparser qui est conçu pour HTML 4.0.

Je dirais qu'il est sûr de supposer que vous pouvez charger une ISO-8859-1 chaîne codée.

Votre chaîne est - UTF-8 codé. Transformer tous les caractères supérieur à 127 / h7F en Entités HTML et que vous êtes bien. Si vous ne voulez pas faire votre propre, qu'est ce qu' mb_convert_encoding avec l' HTML-ENTITIES cible de codage:

  • Ces personnages qui ont entités nommées, obtenez le nom entitiy. € -> &euro;
  • Les autres faire leur numériques (décimal) de l'entité, par exemple, ☆ -> &#9734;

Ce qui suit est un exemple de code qui rend la progression un peu plus visible en utilisant une fonction de rappel:

$html = preg_replace_callback('/[\x{80}-\x{10FFFF}]/u', function($match) {
    list($utf8) = $match;
    $entity = mb_convert_encoding($utf8, 'HTML-ENTITIES', 'UTF-8');
    printf("%s -> %s\n", $utf8, $entity);
    return $entity;
}, $html);

Exemplaire de sorties pour votre chaîne:

☆ -> &#9734;
☆ -> &#9734;
☆ -> &#9734;

De toute façon, c'est juste pour regarder plus profondément dans votre chaîne. Vous voulez qu'il soit converti en un codage loadHTML . Qui peut être fait par la conversion de tous à l'extérieur de US-ASCII en Entités HTML:

$us_ascii = mb_convert_encoding($utf_8, 'HTML-ENTITIES', 'UTF-8');

Prenez garde que votre entrée est en fait codé en UTF-8. Si vous avez mélangé les encodages (ce qui peut arriver avec certains intrants) mb_convert_encoding ne peut gérer qu'un codage par chaîne. Je l'ai déjà indiqué ci-dessus pour plus spécifiquement de la chaîne de remplacement avec l'aide d'expressions régulières, je laisse donc plus de détails pour l'instant.

L'autre alternative est à la pointe de l'encodage. Cela peut être fait dans votre cas, en modifiant le document et l'ajout d'un

<meta http-equiv="content-type" content="text/html; charset=utf-8">

qui est un Type de Contenu de spécifier un jeu de caractères. C'est aussi la meilleure pratique pour les chaînes HTML qui ne sont pas disponibles par l'intermédiaire d'un serveur web (par exemple enregistré sur le disque ou à l'intérieur d'une chaîne de caractères comme dans votre exemple). Le serveur normalement est ce que l'en-tête de réponse.

Si vous n'avez pas de soins de l'égaré mises en garde, vous pouvez l'ajouter en face de la chaîne:

$dom = new DomDocument();
$dom->loadHTML('<meta http-equiv="content-type" content="text/html; charset=utf-8">'.$html);

Par le HTML 2.0 spécifications, des éléments qui ne peuvent apparaître dans l' <head> section d'un document, il sera automatiquement placé là. C'est ce qui se passe ici, trop. La sortie (pretty-print):

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta charset="utf-8">
    <title>Test!</title>
  </head>
  <body>
    <h1>☆ Hello ☆ World ☆</h1>    
  </body>
</html>

17voto

DeZeA Points 99

Il y a de plus rapide fix pour que, après le chargement de votre document html dans DOMDocument, vous venez de définir (ou pour mieux dire remise à zéro) le codage d'origine. Voici un exemple de code:

$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="UTF-8">' . $html);

foreach ($dom->childNodes as $item)
    if ($item->nodeType == XML_PI_NODE)
        $dom->removeChild($item);
$dom->encoding = 'UTF-8'; // reset original encoding

11voto

<?php
  header("Content-type: text/html; charset=utf-8");
  $html = <<<HTML
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Test!</title>
</head>
<body>
    <h1>☆ Hello ☆ World ☆</h1>
</body>
</html>
HTML;

  $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
  $dom = new DomDocument("1.0", "utf-8");
  $dom->loadHTML($html);

  header("Content-Type: text/html; charset=utf-8");
  echo($dom->saveHTML());

Sortie:

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Test!</title></head><body>
    <h1>&#9734; Hello &#9734; World &#9734;</h1>
</body></html>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X