web-dev-qa-db-fra.com

htmlentities dans PHP mais en préservant les balises html

Je veux convertir tous les textes d'une chaîne en entités html mais en préservant les balises HTML, par exemple ceci:

<p><font style="color:#FF0000">Camión español</font></p>

devrait être traduit en ceci:

<p><font style="color:#FF0000">Cami&oacute;n espa&ntilde;ol</font></p>

des idées?

54
fidoboy

Vous pouvez obtenir la liste des correspondances caractère => entité utilisée par htmlentities , avec la fonction get_html_translation_table ; considérez ce code:

$list = get_html_translation_table(HTML_ENTITIES);
var_dump($list);

(Vous voudrez peut-être vérifier le deuxième paramètre de cette fonction dans le manuel - vous devrez peut-être le définir sur une valeur différente de celle par défaut)

Cela vous donnera quelque chose comme ceci:

array
  ' ' => string '&nbsp;' (length=6)
  '¡' => string '&iexcl;' (length=7)
  '¢' => string '&cent;' (length=6)
  '£' => string '&pound;' (length=7)
  '¤' => string '&curren;' (length=8)
  ....
  ....
  ....
  'ÿ' => string '&yuml;' (length=6)
  '"' => string '&quot;' (length=6)
  '<' => string '&lt;' (length=4)
  '>' => string '&gt;' (length=4)
  '&' => string '&amp;' (length=5)

Maintenant, supprimez les correspondances dont vous ne voulez pas:

unset($list['"']);
unset($list['<']);
unset($list['>']);
unset($list['&']);

Votre liste contient maintenant toutes les correspondances character => entity utilisées par htmlentites, à l'exception des quelques caractères que vous ne voulez pas encoder.

Et maintenant, il suffit d'extraire la liste des clés et des valeurs:

$search = array_keys($list);
$values = array_values($list);

Et, enfin, vous pouvez utiliser str_replace pour effectuer le remplacement:

$str_in = '<p><font style="color:#FF0000">Camión español</font></p>';
$str_out = str_replace($search, $values, $str_in);
var_dump($str_out);

Et vous obtenez:

string '<p><font style="color:#FF0000">Cami&Atilde;&sup3;n espa&Atilde;&plusmn;ol</font></p>' (length=84)

Qui ressemble à ce que vous vouliez ;-)


Edit: eh bien, sauf pour le problème d'encodage (putain UTF-8, je suppose - j'essaie de trouver une solution pour cela, et je vais éditer à nouveau)

Deuxième modification quelques minutes après: il semble que vous devrez utiliser utf8_encode sur le $search liste, avant d'appeler str_replace :-(

Ce qui signifie utiliser quelque chose comme ça:

$search = array_map('utf8_encode', $search);

Entre l'appel à array_keys et l'appel à str_replace.

Et, cette fois, vous devriez vraiment obtenir ce que vous vouliez:

string '<p><font style="color:#FF0000">Cami&oacute;n espa&ntilde;ol</font></p>' (length=70)


Et voici la partie complète du code:

$list = get_html_translation_table(HTML_ENTITIES);
unset($list['"']);
unset($list['<']);
unset($list['>']);
unset($list['&']);

$search = array_keys($list);
$values = array_values($list);
$search = array_map('utf8_encode', $search);

$str_in = '<p><font style="color:#FF0000">Camión español</font></p>';
$str_out = str_replace($search, $values, $str_in);
var_dump($str_in, $str_out);

Et la sortie complète:

string '<p><font style="color:#FF0000">Camión español</font></p>' (length=58)
string '<p><font style="color:#FF0000">Cami&oacute;n espa&ntilde;ol</font></p>' (length=70)

Cette fois, ça devrait être ok ^^
Cela ne tient pas vraiment sur une seule ligne, ce n'est peut-être pas la solution la plus optimisée; mais cela devrait bien fonctionner, et a l'avantage de vous permettre d'ajouter/supprimer n'importe quel caractère de correspondance => entité dont vous avez besoin ou non.

S'amuser !

64
Pascal MARTIN

Peut-être pas terriblement efficace, mais ça marche

$sample = '<p><font style="color:#FF0000">Camión español</font></p>';

echo htmlspecialchars_decode(
    htmlentities($sample, ENT_NOQUOTES, 'UTF-8', false)
  , ENT_NOQUOTES
);
18
Peter Bailey

Il s'agit d'une version optimisée de la réponse acceptée.

$list = get_html_translation_table(HTML_ENTITIES);
unset($list['"']);
unset($list['<']);
unset($list['>']);
unset($list['&']);

$string = strtr($string, $list);
7
SileNT

Aucune solution à court d'un analyseur ne sera correcte dans tous les cas. Le vôtre est un bon cas:

<p><font style="color:#FF0000">Camión español</font></p>

mais voulez-vous également soutenir:

<p><font>true if 5 < a && name == "joe"</font></p>

où vous voulez qu'il sorte:

<p><font>true if 5 &lt; a &amp;&amp; name == &quot;joe&quot;</font></p>

Question: Pouvez-vous faire l'encodage AVANT de construire le HTML. En d'autres termes, peut faire quelque chose comme:

"<p><font>" + htmlentities(inner) + "</font></p>"

Vous vous épargnerez beaucoup de chagrin si vous pouvez le faire. Si vous ne le pouvez pas, vous aurez besoin d'un moyen de sauter l'encodage <,> et "(comme décrit ci-dessus), ou simplement de tout encoder, puis de l'annuler (par exemple. replace('&lt;', '<'))

5
ndp

C'est une fonction que je viens d'écrire qui résout ce problème de manière très élégante:

Tout d'abord, les balises HTML seront extraites de la chaîne, puis htmlentities () est exécuté sur chaque sous-chaîne restante et après cela, les balises HTML d'origine seront insérées à leur ancienne position, ce qui n'entraînera aucune alternance des balises HTML. :-)

S'amuser:

function htmlentitiesOutsideHTMLTags ($htmlText)
{
    $matches = Array();
    $sep = '###HTMLTAG###';

    preg_match_all("@<[^>]*>@", $htmlText, $matches);   
    $tmp = preg_replace("@(<[^>]*>)@", $sep, $htmlText);
    $tmp = explode($sep, $tmp);

    for ($i=0; $i<count($tmp); $i++)
        $tmp[$i] = htmlentities($tmp[$i]);

    $tmp = join($sep, $tmp);

    for ($i=0; $i<count($matches[0]); $i++)
        $tmp = preg_replace("@$sep@", $matches[0][$i], $tmp, 1);

    return $tmp;
}
3
bflesch

Basé sur la réponse de bflesch , j'ai fait quelques changements pour gérer la chaîne contenant less than sign, greater than sign et single quote ou double quotes.

function htmlentitiesOutsideHTMLTags ($htmlText, $ent)
{
    $matches = Array();
    $sep = '###HTMLTAG###';

    preg_match_all(":</{0,1}[a-z]+[^>]*>:i", $htmlText, $matches);

    $tmp = preg_replace(":</{0,1}[a-z]+[^>]*>:i", $sep, $htmlText);
    $tmp = explode($sep, $tmp);

    for ($i=0; $i<count($tmp); $i++)
        $tmp[$i] = htmlentities($tmp[$i], $ent, 'UTF-8', false);

    $tmp = join($sep, $tmp);

    for ($i=0; $i<count($matches[0]); $i++)
        $tmp = preg_replace(":$sep:", $matches[0][$i], $tmp, 1);

    return $tmp;
}



Exemple d'utilisation:

$string = '<b>Is 1 < 4?</b>è<br><i>"then"</i> <div style="some:style;"><p>gain some <strong>€</strong><img src="/some/path" /></p></div>';
$string_entities = htmlentitiesOutsideHTMLTags($string, ENT_QUOTES | ENT_HTML401);
var_dump( $string_entities );

La sortie est:

string '<b>Is 1 &lt; 4?</b>&egrave;<br><i>&quot;then&quot;</i> <div style="some:style;"><p>gain some <strong>&euro;</strong><img src="/some/path" /></p></div>' (length=150)



Vous pouvez passer tout ent flag selon le manuel htmlentities

2
Luca Borrione

solution à une ligne sans table de traduction ou fonction personnalisée requise:

je sais que c'est une vieille question, mais j'ai récemment dû importer un site statique dans un site wordpress et j'ai dû surmonter ce problème:

voici ma solution qui ne nécessite pas de tripoter les tables de traduction:

htmlspecialchars_decode( htmlentities( html_entity_decode( $string ) ) );

lorsqu'il est appliqué à la chaîne OP:

<p><font style="color:#FF0000">Camión español</font></p>

production:

<p><font style="color:#FF0000">Cami&oacute;n espa&ntilde;ol</font></p>

lorsqu'il est appliqué à la chaîne de Luca:

<b>Is 1 < 4?</b>è<br><i>"then"</i> <div style="some:style;"><p>gain some <strong>€</strong><img src="/some/path" /></p></div>

production:

<b>Is 1 < 4?</b>&egrave;<br><i>"then"</i> <div style="some:style;"><p>gain some <strong>&euro;</strong><img src="/some/path" /></p></div>
1
aequalsb