【问题标题】:htmlentities in PHP but preserving html tagsPHP中的htmlentities但保留html标签
【发布时间】:2010-11-24 19:06:06
【问题描述】:

我想将字符串中的所有文本转换为 html 实体,但保留 HTML 标签,例如:

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

应该翻译成这样:

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

有什么想法吗?

【问题讨论】:

  • +1 提出一个好问题,我也想知道!
  • 其实我会说这是个错误的问题。为什么要转义这些字符?
  • 这样的东西可能有用(我喜欢彼得的回答),但问它让我立即怀疑 OP 存在字符编码不匹配问题(通常是 UTF-8 与 ISO-8859-1 ) 应该先修复它,而不是尝试隐藏实体引用背后的破坏性 - 转义在 HTML 中定义的实体的小字符比较到 Unicode 选择。

标签: php html string replace html-entities


【解决方案1】:

可能效率不高,但确实有效

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

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

【讨论】:

    【解决方案2】:

    您可以通过get_html_translation_table函数获取htmlentities使用的对应字符=>实体列表;考虑这段代码:

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

    (您可能需要在手册中检查该函数的第二个参数——也许您需要将其设置为不同于默认值的值)

    它会给你这样的东西:

    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)
    

    现在,删除你不想要的对应:

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

    现在,您的列表包含 htmlentites 使用的所有对应字符 => 实体,除了您不想编码的少数字符。

    现在,您只需提取键和值列表:

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

    最后,您可以使用 str_replace 进行替换:

    $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);
    

    你会得到:

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

    这看起来像你想要的 ;-)


    编辑:好吧,除了编码问题(该死的 UTF-8,我想——我正在尝试找到解决方案,并将再次编辑) p>

    几分钟后的第二次编辑:看来您必须在$search 列表中使用utf8_encode,然后再调用str_replace :-(

    这意味着使用这样的东西:

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

    在对array_keys 的调用和对str_replace 的调用之间。

    而且,这一次,你真的应该得到你想要的:

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


    这是代码的完整部分:

    $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);
    

    以及完整的输出:

    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)
    

    这次应该可以了^^
    它并不真正适合一行,可能不是最优化的解决方案;但它应该可以正常工作,并且具有允许您添加/删除任何对应字符 => 您需要或不需要的实体的优点。

    玩得开心!

    【讨论】:

    • 哇,答案很好,做得很好。如果可以的话,我会给你 +3 ;)
    • +1 表示 utf-8 部分。首先使用strtr,这破坏了编码。
    • 这才叫绝妙!!
    • 不需要这么复杂。再往下看我的回答。 htmlspecialchars_decode( htmlentities( html_entity_decode( $string ) ) );
    • 这个 stopped working in PHP 5.4 因为 get_html_translation_table 现在默认返回 UTF-8。您可以根据需要指定不同的编码,但只需从此答案 makes it work again 中删除 utf8_encode。
    【解决方案3】:

    没有解析器的解决方案对于所有情况都是正确的。你的情况很好:

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

    但你是否也想支持:

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

    你希望它出现在哪里:

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

    问题:您可以在构建 HTML 之前进行编码吗?换句话说,可以这样做:

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

    如果你能做到这一点,你会为自己省去很多悲伤。如果你不能,你需要一些方法来跳过编码 和 "(如上所述),或者简单地编码,然后撤消它(例如。replace('&amp;lt;', '&lt;')

    【讨论】:

      【解决方案4】:

      这是我刚刚编写的一个函数,它以一种非常优雅的方式解决了这个问题:

      首先,将从字符串中提取 HTML 标签,然后对每个剩余的子字符串执行 htmlentities(),然后将原始 HTML 标签插入到它们的旧位置,从而不会导致 HTML 标签的交替. :-)

      玩得开心:

      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;
      }
      

      【讨论】:

      • 感谢分享您的解决方案!如果您不介意我对您的代码进行了一些更改,请查看我的回答。
      【解决方案5】:

      这是已接受答案的优化版本。

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

      【讨论】:

      • 更优化:$list = get_html_translation_table(HTML_ENTITIES); unset($list['"'], $list['&lt;'], $list['&gt;'], $list['&amp;']); echo strtr($val, $list);
      【解决方案6】:

      根据bflesch 的回答,我做了一些更改来管理包含less than signgreater than signsingle quotedouble 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;
      }
      



      使用示例:

      $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 );
      

      输出是:

      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)
      



      你可以根据htmlentities manual传递任何ent flag

      【讨论】:

      • 感谢您让我更接近解决方案,但我使用您的解决方案和下面的字符串,这并不完全符合我的要求 - google.com">google</a>

        这是段落

        指定的无效标签应按原样显示粗体小于号 - aaaaa?所有字符
      【解决方案7】:

      无需翻译表或自定义功能的一站式解决方案:

      我知道这是一个老问题,但我最近不得不将一个静态网站导入一个 wordpress 网站并且必须克服这个问题:

      这是我不需要摆弄翻译表的解决方案:

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

      当应用于 OP 的字符串时:

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

      输出:

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

      当应用于 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>
      

      输出:

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

      【讨论】:

      • 我发现这条简单的线路确实运行良好。向下滚动到最近的答案而不是选择的答案通常会很好。
      • 这应该只是htmlspecialchars_decode( htmlentities( $string ) ); - 如果你不删除第三个函数调用,输入字符串&lt;p&gt;The HTML you want is "1 &amp;gt; 0"&lt;/p&gt; 变成&lt;p&gt;The HTML you want is "1 &gt; 0"&lt;/p&gt;,这是不正确的,可能是一个安全漏洞。跨度>
      • @M Somerville -- 不正确...请先完整阅读 OPs 需求。关键是获取一个可能已经编码了实体的字符串,并将其转换为一个带有 HTML 实体的字符串,它们应该在其中维护 HTML 标记——因此你必须有html_entity_decode()——你能解释一下转换 HTML 的安全风险吗?例如,没有提到面向公众的表单提交......边缘情况不在这篇文章的范围内——也许你可以开始一个解决你所指出的问题
      猜你喜欢
      • 1970-01-01
      • 2014-08-26
      • 1970-01-01
      • 2019-05-15
      • 2016-10-09
      • 2021-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多