Необходимо предотвратить регулярное выражение PHP segfault - PullRequest
1 голос
/ 12 ноября 2009

Почему возникает следующий segfault и как я могу его предотвратить?

<?php

$str = ' <fieldset> <label for="go-to">Go to: </label>  ' 
       . str_repeat(' ', 10000) 
       . '<input type="submit" value="Go" /> </fieldset> </form>';

preg_match_all("@
</?(?![bisa]\b)(?!em\b)[^>]*> # starting tag, must not be one of several inline tags
(?:[^<]|</?(?:(?:[bisau]|em|strong|sup)\b)[^>]*>)* #allow text and some inline tags
[\?\!\.]+
@ix", $str, $matches);

?>

Я полагаю, что это вызывает .... ожидание .... переполнение стека.

EDIT:

Выше приведен упрощенный вариант шаблона, демонстрирующий проблему. Более полная версия:

@
</?(?![bisa]\b)(?!em\b)[^>]*> # starting tag, must not be one of several inline tags
(?:[^<]|</?(?:(?:[bisau]|em|strong|sup)\b)[^>]*>)* # continue, allow text content and some inline tags

# normal sentence ending
[\?\!\.]+ # valid ending characters -- note elipses allowed
(?<!\b[ap]m\.)(?<!\b[ap]\.m\.)(?<!digg this\!)(?<!Stumble This\!) # disallow some  false positives that we don't care about
\s*
(?:&apos;|&\#0*34;|'|&lsquo;)?\s* # closing single quotes, in the unusual case like "he said: 'go away'".
(?:"|&quot;|&\#0*34;|&\#x0*22;|&rdquo;|&\#0*8221;|&\#x0*201D;|''|``|\xe2\x80\x9d|&\#0*148;|&\#x0*94;|\x94|\))?\s* # followed by any kind of close-quote char
(?=\<) # should be followed by a tag.
@ix

Цель состоит в том, чтобы найти html-блоки, которые заканчиваются тем, что похоже на правильное окончание английского предложения. Я обнаружил, что этот метод очень хорошо показывает разницу между текстом «контент» (например, текст статьи) и текстом «макет» (т. Е. Как элементы навигации). Иногда, если между тегами имеется огромное количество пустого пространства, оно взрывается.

Ответы [ 4 ]

2 голосов
/ 12 ноября 2009

Первое, что я бы попробовал, это сделать все квантификаторы собственническими и все группы атомарными:

"@</?+(?![bisa]\b)(?!em\b)[^>]*+>
(?>[^<]++|</?+(?>(?>[bisau]|em|strong|sup)\b)[^>]*+>)*+
[?!.]+
@ix"

Я думаю, что Джереми прав: это не возвращение назад само по себе убивает вас, это вся информация о состоянии, которую движок регулярных выражений должен сохранить, чтобы сделать возможным возврат. Регулярное выражение, похоже, построено таким образом, что, если ему когда-либо придется вернуться, оно все равно потерпит неудачу. Так что используйте собственнические квантификаторы и атомарные группы и не беспокойтесь о сохранении всей этой бесполезной информации.

РЕДАКТИРОВАТЬ: чтобы допустить пунктуацию, заканчивающуюся предложением, вы можете добавить еще одну альтернативу ко второй строке:

(?>[^<?!.]++|(?![^?!.\s<]++<)[?!.]++|</?+(?>(?>[bisau]|em|strong|sup)\b)[^>]*+>)*+

Добавление соответствует одному или нескольким из указанных символов, если только они не являются последними непробельными символами в элементе.

1 голос
/ 12 ноября 2009

Я вполне уверен, что даже более новые версии PHP связаны с PCRE 7.0, в которой обнаружены проблемы с сегментами. Я не думаю, что есть какие-либо намерения исправить проблему, поскольку технически это проблема PCRE, а не проблема с PHP.

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

Ошибка: http://bugs.php.net/bug.php?id=40909

0 голосов
/ 12 ноября 2009

Ваше регулярное выражение вызывает огромное количество возвратов. С 10000 символов в середине, это будет довольно грязно и медленно. Тем не менее, я не ожидал бы, что это потерпит крах ...!

0 голосов
/ 12 ноября 2009

Это все еще делает то, что вы хотите?

</?(?![bisa]\b)(?!em\b)[^>]*> # starting tag, must not be one of several inline tags
(?:(?>[^<\?\!\.]*)|</?(?:(?:[bisau]|em|strong|sup)\b)[^>]*>)* #allow text and some inline tags
[\?\!\.]+
...