【问题标题】:How do I prevent Php's DOMDocument from encoding html entities?如何防止 PHP 的 DOMDocument 编码 html 实体?
【发布时间】:2010-10-21 23:56:07
【问题描述】:

我有一个使用 PHP 的 DOMDocument 替换字符串中锚点的 href 属性的函数。这是一个sn-p:

$doc        = new DOMDocument('1.0', 'UTF-8');
$doc->loadHTML($text);
$anchors    = $doc->getElementsByTagName('a');

foreach($anchors as $a) {
    $a->setAttribute('href', 'http://google.com');
}

return $doc->saveHTML();

问题在于 loadHTML($text) 在 doctype、html、body 等标签中包围了 $text。我尝试通过这样做而不是 loadHTML() 来解决这个问题:

$doc        = new DOMDocument('1.0', 'UTF-8');
$node       = $doc->createTextNode($text);
$doc->appendChild($node);
...

不幸的是,这会编码所有实体(包括锚点)。有谁知道如何关闭这个?我已经彻底查看了文档并尝试破解它,但无法弄清楚。

谢谢! :)

【问题讨论】:

  • 对于 2022 年访问此内容的所有人:从 libxml 2.6 开始,loadHTML 支持防止添加周围 HTML 标记的选项:$contentDom->loadHTML($text, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

标签: php anchor href domdocument createtextnode


【解决方案1】:
$text 是带有占位符锚标记的翻译字符串

如果这些占位符具有严格、定义明确的格式,则简单的 preg_replacepreg_replace_callback 可能会解决问题。
我不建议在一般情况下使用正则表达式来摆弄 html 文档,但对于定义明确的小子集,它们是合适的。

【讨论】:

    【解决方案2】:

    XML 只有very few predefined entities。您所有的 html 实体都在其他地方定义。当您使用 loadhtml() 时,这些实体定义会自动加载,而使用 loadxml()(或根本没有 load())则不会。
    createTextNode() 完全符合其名称的含义。您作为值传递的所有内容都被视为文本内容,而不是标记。 IE。如果您将具有特殊含义的内容传递给标记(、...),则它的编码方式是解析器可以将文本与实际标记(<、>、...)区分开来

    $text 是从哪里来的?你不能在实际的 html 文档中进行替换吗?

    【讨论】:

    • loadHTML,没有实体翻译发生。我最终通过运行 mb_substr($text, 122, -19); 以一种脆弱的方式解决了这个问题。 $doc->saveHTML() 的结果。哎呀! :) $text 是带有占位符锚标记的翻译字符串,因此必须在运行时进行替换。我宁愿不解析整个文档,因为仅解析翻译的链接会很困难。好主意。
    【解决方案3】:

    我最终以一种微不足道的方式破解了这个,改变了:

    return $doc->saveHTML();
    

    进入:

    $text       = $doc->saveHTML();
    return mb_substr($text, 122, -19);
    

    这消除了所有不必要的垃圾,改变了这一点:

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" 
    "http://www.w3.org/TR/REC-html40/loose.dtd"> <html><body><p>
    You can <a href="http://www.google.com">click here</a> to visit Google.</p>
    </body></html> 
    

    进入这个:

    You can <a href="http://www.google.com">click here</a> to visit Google.
    

    谁能找到更好的办法?

    【讨论】:

      【解决方案4】:

      好的,这是我最终得到的解决方案。决定听从 VolkerK 的建议。

      public static function ReplaceAnchors($text, array $attributeSets)
      {
          $expression = '/(<a)([\s\w\d:\/=_&\[\]\+%".?])*(>)/';
      
          if (empty($attributeSets) || !is_array($attributeSets)) {
              // no attributes to set. Set href="#".
              return preg_replace($expression, '$1 href="#"$3', $text);
          }
      
          $attributeStrs  = array();
          foreach ($attributeSets as $attributeKeyVal) {
              // loop thru attributes and set the anchor
              $attributePairs = array();
              foreach ($attributeKeyVal as $name => $value) {
                  if (!is_string($value) && !is_int($value)) {
                      continue; // skip
                  }
      
                  $name               = htmlspecialchars($name);
                  $value              = htmlspecialchars($value);
                  $attributePairs[]   = "$name=\"$value\"";
              }
              $attributeStrs[]    = implode(' ', $attributePairs);
          }
      
          $i      = -1;
          $pieces = preg_split($expression, $text);
          foreach ($pieces as &$piece) {
              if ($i === -1) {
                  // skip the first token
                  ++$i;
                  continue;
              }
      
              // figure out which attribute string to use
              if (isset($attributeStrs[$i])) {
                  // pick the parallel attribute string
                  $attributeStr   = $attributeStrs[$i];
              } else {
                  // pick the last attribute string if we don't have enough
                  $attributeStr   = $attributeStrs[count($attributeStrs) - 1];
              }
      
              // build a opening new anchor for this token.
              $piece  = '<a '.$attributeStr.'>'.preg_replace($expression, '$1 href="#"$3', $piece);
              ++$i;
          }
      
          return implode('', $pieces);
      

      这允许使用一组不同的锚属性调用函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-12
        • 1970-01-01
        • 2016-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-06
        • 1970-01-01
        相关资源
        最近更新 更多