Разбор HTML-комментариев с помощью PHP, но без условных выражений - PullRequest
6 голосов
/ 18 июня 2009

В настоящее время я использую PHP и регулярное выражение для удаления всех комментариев HTML со страницы. Сценарий работает хорошо ... слишком хорошо. Он удаляет все комментарии, включая мои условные комментарии в. Вот что у меня есть:

<?php
  function callback($buffer)
  {
        return preg_replace('/<!--(.|\s)*?-->/', '', $buffer);
  }

  ob_start("callback");
?>
... HTML source goes here ...
<?php ob_end_flush(); ?>

Поскольку мое регулярное выражение не слишком горячее, у меня возникают проблемы с попыткой выяснить, как изменить шаблон, чтобы исключить условные комментарии, такие как:

<!--[if !IE]><!-->
<link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen" />
<!-- <![endif]-->

<!--[if IE 7]>
<link rel="stylesheet" href="/css/ie7.css" type="text/css" media="screen" />
<![endif]-->

<!--[if IE 6]>
<link rel="stylesheet" href="/css/ie6.css" type="text/css" media="screen" />
<![endif]-->

Приветствия

Ответы [ 5 ]

23 голосов
/ 18 июня 2009

Поскольку комментарии не могут быть вложены в HTML, регулярное выражение может сделать эту работу теоретически. Тем не менее, использование какого-либо синтаксического анализатора было бы лучшим выбором, особенно если ваш ввод не гарантированно правильно сформирован.

Вот моя попытка. Чтобы соответствовать только обычные комментарии, это будет работать. Это стало настоящим чудовищем, извините за это. Я проверил это достаточно широко, кажется, что это хорошо, но я не даю никаких гарантий.

<!--(?!\s*(?:\[if [^\]]+]|<!|>))(?:(?!-->).)*-->

Пояснение:

<!--                #01: "<!--"
(?!                 #02: look-ahead: a position not followed by:
  \s*               #03:   any number of space
  (?:               #04:   non-capturing group, any of:
    \[if [^\]]+]    #05:     "[if ...]"
    |<!             #06:     or "<!"
    |>              #07:     or ">"
  )                 #08:   end non-capturing group
)                   #09: end look-ahead
(?:                 #10: non-capturing group:
  (?!-->)           #11:   a position not followed by "-->"
  .                 #12:   eat the following char, it's part of the comment
)*                  #13: end non-capturing group, repeat
-->                 #14: "-->"

Шаги № 02 и № 11 имеют решающее значение. # 02 гарантирует, что следующие символы не обозначают условный комментарий. После этого # 11 гарантирует, что следующие символы не обозначают конец комментария, в то время как # 12 и # 13 вызывают фактическое соответствие.

Применить с флагами "global" и "dotall".

Чтобы сделать обратное (сопоставлять только условные комментарии), это будет примерно так:

<!(--)?(?=\[)(?:(?!<!\[endif\]\1>).)*<!\[endif\]\1>

Пояснение:

<!                  #01: "<!"
(--)?               #02: two dashes, optional
(?=\[)              #03: a position followed by "["
(?:                 #04: non-capturing group:
  (?!               #05:   a position not followed by
    <!\[endif\]\1>  #06:     "<![endif]>" or "<![endif]-->" (depends on #02)
  )                 #07:   end of look-ahead
  .                 #08:   eat the following char, it's part of the comment
)*                  #09: end of non-capturing group, repeat
<!\[endif\]\1>      #10: "<![endif]>" or "<![endif]-->" (depends on #02)

Снова примените с флагами "global" и "dotall".

Шаг № 02 из-за синтаксиса «раскрытого уровня», см .: «MSDN - Об условных комментариях» .

Я не совсем уверен, где пробелы разрешены или ожидаются. При необходимости добавьте \s* к выражению.

2 голосов
/ 18 июня 2009

Если вы не можете заставить его работать с одним регулярным выражением или хотите сохранить больше комментариев, вы можете использовать preg_replace_callback. Затем вы можете определить функцию для индивидуальной обработки комментариев.

<?php
function callback($buffer) {
    return preg_replace_callback('/<!--.*-->/U', 'comment_replace_func', $buffer);
}

function comment_replace_func($m) {
    if (preg_match( '/^\<\!--\[if \!/i', $m[0])) {
        return $m[0];   
    }              

    return '';
}   

ob_start("callback");
?>

... HTML source goes here ...

<?php ob_end_flush(); ?>
1 голос
/ 23 июня 2009

В целом, это, кажется, лучшее решение:

<?php
  function callback($buffer) {
    return preg_replace('/<!--[^\[](.|\s)*?-->/', '', $buffer);
  }
  ob_start("callback");
?>
... HTML source goes here ...
<?php ob_end_flush(); ?>

Он удаляет все комментарии и оставляет условные выражения, за исключением верхнего:

<!--[if !IE]><!-->
<link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen" />
<!-- <![endif]-->

там, где кажется, что дополнительное вызывает проблему.

Если кто-то может предложить регулярное выражение, которое бы учитывало это и оставляло это условие также на месте, тогда это было бы идеально.

Решение Томалака выглядит хорошо, но как новичок и без дальнейших указаний я не знаю, как его реализовать, хотя я хотел бы попробовать, если кто-нибудь может уточнить, как его применить?

Спасибо

0 голосов
/ 18 июня 2009

Я не уверен, понравится ли движку PHP регулярное выражение, но попробуйте этот шаблон:

'/<!--(.|\s)*(\[if .*\]){0}(.|\s)*?-->/'
0 голосов
/ 18 июня 2009

Примерно так может работать:

/<!--[^\[](.|\s)*?-->/

Это то же самое, что и у вас, за исключением того, что комментарии игнорируются с открывающей скобкой сразу после тега начала комментария.

...