RegExp в функции preg_match возвращает ошибку браузера

Следующая функция прерывается с регулярным выражением, которое я предоставил в переменной $ pattern. Если я изменил regexp, я в порядке, поэтому я думаю, что это проблема. Я не вижу проблемы, хотя, и я не получаю стандартную ошибку PHP, даже если они включены.

function parseAPIResults($results){ //Takes results from getAPIResults, returns array. $pattern = '/\[(.|\n)+\]/'; $resultsArray = preg_match($pattern, $results, $matches); } 

Firefox 6: соединение было сброшено

Chrome 14: Ошибка 101 (net :: ERR_CONNECTION_RESET): соединение было сброшено.

IE 8: Internet Explorer не может отображать веб-страницу

ОБНОВИТЬ:
Apache / PHP может быть сбой. Вот журнал ошибок Apache при запуске скрипта:

[Сб Окт 01 11:41:40 2011] [уведомление] Родитель: дочерний процесс завершен со статусом 255 – Перезапуск.
[Сб окт 01, 11:41:40 2011] [уведомление] Apache / 2.2.11 (Win32) Настройка PHP / 5.3.0 – возобновление обычных операций

Запуск WAMP 2.0 в Windows 7.

Простой вопрос. Комплексный ответ!

Да, этот класс регулярных выражений будет повторяться (и молча) сбой Apache / PHP с необработанной ошибкой сегментации из-за переполнения стека!

Задний план:

Семейство регулярных выражений PHP preg_* использует мощную библиотеку PCRE Филиппа Хейзеля. В этой библиотеке существует определенный класс регулярных выражений, который требует много рекурсивных вызовов для своей внутренней функции match() и это использует много пространства стека (и используемое пространство стека прямо пропорционально размеру строки субъекта соответствие). Таким образом, если строка субъекта слишком длинная, произойдет переполнение стека и соответствующая ошибка сегментации. Такое поведение описано в документации PCRE в конце под рубрикой «pcrestack» .

PHP Bug 1: PHP устанавливает: pcre.recursion_limit слишком большой.

Документация PCRE описывает, как избежать ошибки сегментации переполнения стека, ограничивая глубину рекурсии до безопасного значения, примерно равного размеру стека связанного приложения, деленного на 500. Когда глубина рекурсии должным образом ограничена, как рекомендовано, библиотека не генерирует переполнение стека и вместо этого изящно выходит с кодом ошибки. В PHP эта максимальная глубина рекурсии задается с помощью переменной конфигурации pcre.recursion_limit и (к сожалению) значение по умолчанию установлено равным 100 000. Это значение слишком много! Ниже приведена таблица безопасных значений pcre.recursion_limit для различных размеров исполняемых стеков:

 Stacksize pcre.recursion_limit 64 MB 134217 32 MB 67108 16 MB 33554 8 MB 16777 4 MB 8388 2 MB 4194 1 MB 2097 512 KB 1048 256 KB 524 

Таким образом, для сборки Win32 веб-сервера Apache ( httpd.exe ), который имеет (относительно небольшой) размер стека 256 КБ, правильное значение pcre.recursion_limit должно быть установлено равным 524. Это может быть выполнено с помощью следующей строки PHP-код:

 ini_set("pcre.recursion_limit", "524"); // PHP default is 100,000. 

Когда этот код добавляется к скрипту PHP, переполнение стека НЕ ​​происходит, а вместо этого генерирует значимый код ошибки. То есть, СЛЕДУЕТ генерировать код ошибки! (Но, к сожалению, из-за другой ошибки PHP, preg_match() не делает.)

Ошибка PHP 2: preg_match() не возвращает FALSE при ошибке.

Документация PHP для preg_match() говорит, что она возвращает FALSE при ошибке. К сожалению, в версиях PHP 5.3.3 и ниже есть ошибка ( # 52732 ), где preg_match() НЕ возвращает FALSE при ошибке (вместо этого он возвращает int(0) , что является тем же значением, возвращаемым в случае несоответствия) , Эта ошибка была исправлена ​​в PHP версии 5.3.4.

Решение:

Предполагая, что вы продолжите использовать WAMP 2.0 (с PHP 5.3.0), решение должно учитывать оба вышеупомянутых ошибки. Вот что я бы рекомендовал:

  • Необходимо уменьшить pcre.recursion_limit до безопасного значения: 524.
  • Необходимо явно проверять наличие ошибки PCRE всякий раз, когда preg_match() возвращает что-либо, кроме int(1) .
  • Если preg_match() возвращает int(1) , то совпадение было успешным.
  • Если preg_match() возвращает int(0) , то совпадение было либо неудачным, либо произошла ошибка.

Ниже приведена модифицированная версия вашего сценария (предназначенная для запуска из командной строки), которая определяет длину строки темы, которая приводит к ошибке ограничения рекурсии:

 <?php // This test script is designed to be run from the command line. // It measures the subject string length that results in a // PREG_RECURSION_LIMIT_ERROR error in the preg_match() function. echo("Entering TEST.PHP...\n"); // Set and display pcre.recursion_limit. (set to stacksize / 500). // Under Win32 httpd.exe has a stack = 256KB and 8MB for php.exe. //ini_set("pcre.recursion_limit", "524"); // Stacksize = 256KB. ini_set("pcre.recursion_limit", "16777"); // Stacksize = 8MB. echo(sprintf("PCRE pcre.recursion_limit is set to %s\n", ini_get("pcre.recursion_limit"))); function parseAPIResults($results){ $pattern = "/\[(.|\n)+\]/"; $resultsArray = preg_match($pattern, $results, $matches); if ($resultsArray === 1) { $msg = 'Successful match.'; } else { // Either an unsuccessful match, or a PCRE error occurred. $pcre_err = preg_last_error(); // PHP 5.2 and above. if ($pcre_err === PREG_NO_ERROR) { $msg = 'Successful non-match.'; } else { // preg_match error! switch ($pcre_err) { case PREG_INTERNAL_ERROR: $msg = 'PREG_INTERNAL_ERROR'; break; case PREG_BACKTRACK_LIMIT_ERROR: $msg = 'PREG_BACKTRACK_LIMIT_ERROR'; break; case PREG_RECURSION_LIMIT_ERROR: $msg = 'PREG_RECURSION_LIMIT_ERROR'; break; case PREG_BAD_UTF8_ERROR: $msg = 'PREG_BAD_UTF8_ERROR'; break; case PREG_BAD_UTF8_OFFSET_ERROR: $msg = 'PREG_BAD_UTF8_OFFSET_ERROR'; break; default: $msg = 'Unrecognized PREG error'; break; } } } return($msg); } // Build a matching test string of increasing size. function buildTestString() { static $content = ""; $content .= "A"; return '['. $content .']'; } // Find subject string length that results in error. for (;;) { // Infinite loop. Break out. $str = buildTestString(); $msg = parseAPIResults($str); printf("Length =%10d\r", strlen($str)); if ($msg !== 'Successful match.') break; } echo(sprintf("\nPCRE_ERROR = \"%s\" at subject string length = %d\n", $msg, strlen($str))); echo("Exiting TEST.PHP..."); ?> 

Когда вы запускаете этот скрипт, он обеспечивает непрерывное считывание текущей длины строки темы. Если pcre.recursion_limit остается с слишком высоким значением по умолчанию, это позволяет измерить длину строки, которая приводит к сбою исполняемого файла.

Комментарии:

  • Прежде чем исследовать ответ на этот вопрос, я не знал о ошибке PHP, где preg_match() не смог вернуть FALSE когда в библиотеке PCRE возникла ошибка. Эта ошибка, безусловно, ставит под вопрос LOT кода, который использует preg_match ! (Я обязательно сделаю инвентаризацию своего собственного PHP-кода.)
  • В Windows исполняемый файл веб-сервера Apache ( httpd.exe ) построен со стекизацией 256 КБ. Исполняемый файл командной строки PHP ( php.exe ) построен со стекированием 8 МБ. Безопасное значение для pcre.recursion_limit должно быть установлено в соответствии с исполняемым файлом, что сценарий запускается в соответствии с (524 и 16777 соответственно).
  • В системах * nix, веб-сервер Apache и исполняемые файлы командной строки, как правило, построены с помощью стекирования 8 МБ, поэтому эта проблема встречается не так часто.
  • Разработчики PHP должны установить значение pcre.recursion_limit по умолчанию для безопасного значения.
  • Разработчики PHP должны применить исправление preg_match() к PHP версии 5.2.
  • Стекирование исполняемого файла Windows можно вручную изменить с помощью бесплатной программы CFF Explorer . Вы можете использовать эту программу для увеличения httpd.exe исполняемого файла Apache httpd.exe . (Это работает под XP, но Vista и Win7 могут жаловаться.)

Я столкнулся с той же проблемой. Большое спасибо за ответ, отправленный ridgerunner.

Хотя полезно знать, почему сбой php, для меня это на самом деле не решает проблему. Чтобы решить проблему, мне нужно настроить мое регулярное выражение, чтобы сохранить память, чтобы php больше не разбивался.

Поэтому вопрос заключается в том, как изменить регулярное выражение. Ссылка на приведенное выше руководство PCRE уже описывает решение для примера regex, которое очень похоже на ваше.

Итак, как исправить ваше регулярное выражение? Во-первых, вы говорите, что хотите сопоставить «a. Или новую строку». Обратите внимание, что "." является особым символом в регулярном выражении, который не только соответствует точке, но и любому персонажу, поэтому вам нужно избежать этого. (Надеюсь, я не ошибся здесь, и это было предназначено.)

 $pattern = '/\[(\.|\n)+\]/'; 

Затем мы можем скопировать квантификатор внутри скобок:

 $pattern = '/\[(\.+|\n+)+\]/'; 

Это не меняет значения выражения. Теперь мы используем притяжательные кванторы вместо обычных:

 $pattern = '/\[(\.++|\n++)++\]/'; 

Таким образом, это должно иметь то же значение, что и исходное регулярное выражение, но работать на php, не разбивая его. Зачем? Потенциальные кванторы «съедают» персонажей и не позволяют отступать. Поэтому PCRE не нужно использовать рекурсию, и стек не будет переполняться. Использование их внутри скобок кажется хорошей идеей, так как нам часто не нужна количественная оценка альтернативы.

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

  • если возможно, используйте притяжательные кванторы. Это означает: ++, * +,? + {} + Вместо +, *,?, {}.
  • перемещать кванторы внутри альтернативных скобок, где это возможно

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

У меня была одна и та же проблема, и вам нужно привязать шаблон к чему-то вроде

 $pattern = '|/your pattern/|s'; 

«S» на конце в основном означает рассматривать строку как одну строку.

preg_match возвращает количество совпадений, найденных для шаблона. Если у вас есть совпадение, это приводит к фатальной ошибке в php (например, print_r(1) , вызывает ошибку). print_r (0) (если вы меняете шаблон и не имеете совпадений) не выводит и не выводит 0.

Вы хотите print_r($matches)

Как в сторону, ваш шаблон не сбежал должным образом. Использование двойных кавычек означает, что вам нужно избежать обратных косых черт перед вашими скобками.