Я бы хотел помочь.Я видел эту проблему раньше!
Ваше регулярное выражение выглядит логично A-Ok, но при применении к строке темы большого размера, это, вероятно, приводит к рекурсивному обратному отслеживанию, которое вызывает стекпереполнение в двигателе PCRE.Это переполнение приводит к ошибке сегментации и падению исполняемого файла PCRE (Apache или PHP) без предупреждения.(Симптомом является сообщение «соединение закрыто удаленным сервером» .) Этот необработанный сбой происходит из-за неправильного выбора PHP параметра по умолчанию для параметра pcre.recursion_limit
(по умолчанию 100 000, что слишкомвысоко).Сначала давайте посмотрим, является ли это на самом деле частью проблемы.
Добавьте следующий код в ваш скрипт:
// Place this at the top of the script
ini_set("pcre.recursion_limit", "524"); // 256KB stack. Win32 Apache
$re = '#<(code|pre)([^>]*)>(((?!</?\1).)*|(?R))*</\1>#si';
$text = preg_replace_callback($re, 'self::replaceit', $text);
// Check the return value for NULL which indicates a PCRE error.
if ($text === null) exit("PCRE Error! Subject too large or complex.");
С этим на месте вы больше не должны получать "соединение"закрытое »сообщение, а скорее сообщение об ошибке PCRE.Обратите внимание, что вышеуказанный параметр 524 предназначен для Win32 Apache httpd.exe
(который имеет стек 256 КБ).Если вы работаете на сервере * nix, вы можете увеличить это значение до 16777. Причина этих чисел заключается в том, что значение recursion _limit
должно быть установлено на размер исполняемого стека, деленный на 500. Исполняемый файл WIn32 обычно имеет стек 256 КБ.и исполняемые файлы * nix обычно создаются со стеком 8 МБ.Филипп Хейзел (автор механизма превосходный PCRE) подробно рассмотрел эту проблему.См .: Страница справочника pcrestack
Как только вы это сделаете, сообщите об этом, и я помогу с следующей фазой ...
(Обратите внимание, что это НЕ(?R)
выражение, вызывающее проблему. Более позднее.)
Регулярное выражение может быть значительно улучшено (как для решения этой проблемы, так и для повышения его скорости), путем реализации "Джеффри Фридла" Unrolling-the-Петля " Эффективность техники.Это значительно сократит количество необходимых возвратов и, вероятно, решит вашу проблему.Вот улучшенная (и тщательно прокомментированная) версия вашего регулярного выражения.
$re = '% # Match an outermost PRE or CODE element.
( # $1: PRE/CODE element open tag
<(code|pre) # $2: Open tag name
[^>]*+> # Remainder of opening tag.
) # End $1: PRE/CODE element open tag.
( # $3: PRE/CODE element contents.
(?: # Group for contents alternatives
(?R) # Either a nested PRE or CODE element
| # Or non- <CODE, </CODE, <PRE or </PRE stuff.
[^<]*+ # Begin: {normal* (special normal*)*} construct
(?: # See: "Mastering Regular Expressions".
< # {special} Match a <, but only if it is
(?!/?\2) # not the start of a nested or closing tag.
[^<]*+ # match more {normal*}
)*+ # Finish "Unrolling the loop"
)*+ # Zero or more contents alternatives.
) # End $3: PRE/CODE element contents.
(</\2>) # $4: PRE/CODE element close tag
%ix';
Однако это регулярное выражение отличается тем, что оно использует четыре группы захвата: $1
содержит весь начальный тег элемента, $2
содержитимя тега элемента (которое используется в качестве обратной ссылки), $3
содержит содержимое элемента, а $4
содержит конечный тег элемента.