【问题标题】:PHP - preg_replace - html tags and attributesPHP - preg_replace - html 标签和属性
【发布时间】:2019-03-12 03:22:04
【问题描述】:

我正在尝试允许使用数组的一些标签和属性,并删除其余的

这是我的例子:

$allowed=array("img", "p", "style");
$text='<img src="image.gif" onerror="myFunction()" style="background:red" onclick="myFunction()">

<p>A function is triggered if an error occurs when loading the image. The function shows an alert box with a text.
In this example we refer to an image that does not exist, therefore the onerror event occurs.</p>

<script>
function myFunction() {
  alert(\'The image could not be loaded.\');
}
</script>';

使用$text= preg_replace('#&lt;script(.*?)&gt;(.*?)&lt;/script&gt;#is', '', $text); 我可以删除带有内容的脚本标签,但我需要删除不在 $allowed 数组中的所有内容

【问题讨论】:

  • 你可以使用我写的这个 HTML 压缩器,通过一些工作就可以删除特定的标签。它具有不缩小特定标签的能力。因此,您可以将其更改为删除它们(可能)github.com/ArtisticPhoenix/MISC/blob/master/Lexers/… 它以 Lexer/Parser 类型的方式使用正则表达式。
  • @ArtisticPhoenix ini_set('display_errors', 1); 不应在生产环境中使用,可能需要在该 GIT 中添加评论。
  • 这并不是真正的生产代码,它实际上是这里的另一个答案。这就是为什么它在 MISC 中。它确实说//For debugging
  • 有嵌套标签吗?例如&lt;div&gt;&lt;p&gt;text&lt;/p&gt;&lt;img /&gt;more text&lt;p&gt;text&lt;/p&gt;&lt;/div&gt;
  • 是的,内容来自文本编辑器

标签: php preg-replace


【解决方案1】:

如果您像这样将脚本与 html 完全混合,我建议使用 DOMParser 以获得更好的可读性,如果性能很重要,请注意性能。

http://php.net/manual/en/class.domdocument.php

【讨论】:

    【解决方案2】:

    这个函数应该做你想做的事。给定一个 DOMDocument ($doc) 和一个要搜索的节点 ($node),它递归地遍历该节点的子节点,删除不在 $allowed_tags 数组中的所有标签,并且对于这些标签保留,删除不在 $allowed_attributes 数组中的任何属性:

    function remove_nodes_and_attributes($doc, $node, $allowed_tags, $allowed_attributes) {
        $xpath = new DOMXPath($doc);
        foreach ($xpath->query('./*', $node) as $child) {
            if (!in_array($child->nodeName, $allowed_tags)) {
                $node->removeChild($child);
                continue;
            }
            $a = 0;
            while ($a < $child->attributes->length) {
                $attribute = $child->attributes->item($a)->name;
                if (!in_array($attribute, $allowed_attributes)) {
                    $child->removeAttribute($attribute);
                    // don't increment the pointer as the list will shift with the removal of the attribute
                }
                else {
                    // allowed attribute, skip it
                    $a++;
                }
            }
            // remove any children as necessary
            remove_nodes_and_attributes($doc, $child, $allowed_tags, $allowed_attributes);
        }
    }
    

    你会像这样使用这个函数。请注意,有必要将 HTML 包装在顶级元素中,然后在最后使用 substr 再次剥离。

    $doc = new DOMDocument();
    $doc->loadHTML("<html>$text</html>", LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    $html = $doc->getElementsByTagName('html')[0];
    remove_nodes_and_attributes($doc, $html, $allowed_tags, $allowed_attributes);
    echo substr($doc->saveHTML(), 6, -8);
    

    输出(用于您的样本数据):

    <img style="background:red">
    <p>A function is triggered if an error occurs when loading the image. The function shows an alert box with a text. In this example we refer to an image that does not exist, therefore the onerror event occurs.</p>
    

    Demo on 3v4l.org

    【讨论】:

      【解决方案3】:

      使用 DOMDocument 始终是处理 HTML 的最佳方式,它了解文档的结构。

      在这个解决方案中,我使用 XPath 来查找任何不在允许列表中的节点,XPath 表达式将类似于...

      //body//*[not(name() = "img" or name() = "p" or name() = "style")]
      

      这会在&lt;body&gt; 标记(loadHTML 将自动为您放入此标记)中查找名称不在允许标记列表中的任何元素。 XPath 是从$allowed 列表动态构建的,因此您只需更改标签列表即可对其进行更新...

      $allowed=array("img", "p", "style");
      $text='<img src="image.gif" onerror="myFunction()" style="background:red" onclick="myFunction()">
      
      <p>A function is triggered if an error occurs when loading the image. The function shows an alert box with a text.
      In this example we refer to an image that does not exist, therefore the onerror event occurs.</p>
      
      <script>
      function myFunction() {
        alert(\'The image could not be loaded.\');
      }
      </script>';
      
      $doc = new DOMDocument();
      $doc->loadHTML($text);
      $xp = new DOMXPath($doc);
      $find = '//body//*[not(name() = "'.implode ('" or name() = "', $allowed ).
          '")]';
      echo "XPath = ".$find.PHP_EOL;
      $toRemove = $xp->evaluate($find);
      print_r($toRemove);
      foreach ( $toRemove as $remove )    {
          $remove->parentNode->removeChild($remove);
      }
      
      // recreate HTML
      $outHTML = "";
      foreach ( $doc->getElementsByTagName("body")[0]->childNodes as $tag )  {
          $outHTML.= $doc->saveHTML($tag);
      }
      echo $outHTML;
      

      如果您还想删除属性,可以使用 @* 作为 XPath 表达式的一部分来执行相同的过程...

      $allowedAttribs = array();
      
      $find = '//body//@*[not(name() = "'.implode ('" or name() = "', $allowedAttribs ).
      '")]';
      $toRemove = $xp->evaluate($find);
      foreach ( $toRemove as $remove ) {
          $remove->parentNode->removeAttribute($remove->nodeName);
      }
      

      可以将这两者结合起来,但它会使代码变得不那么清晰(恕我直言)。

      【讨论】:

      • 这不会删除 img 标签中的属性(srconclickonerror):3v4l.org/1RR3A
      • @Nick - 我已经添加了这个。如果他们想要某些元素类型的某些属性会很有趣 - 或者我不应该提到:-/
      • 我认为不问这种问题总是最安全的,你可能会得到答案! :-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-13
      • 2012-01-01
      • 1970-01-01
      相关资源
      最近更新 更多