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: соединение было reset

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

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

ОБНОВЛЕНИЕ:
Apache/PHP может быть сбой. Вот журнал ошибок Apache, когда я запускаю script:

[Сб окт 01, 11:41:40 2011] [уведомление] Родитель: дочерний процесс завершен статус 255 - Перезапуск.

[Sat Oct 01 11:41:40 2011] [уведомление] Apache/2.2.11 (Win32) Настройка PHP/5.3.0 - возобновление нормальной работы операции

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

Ответы

Ответ 1

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

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

История:

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

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

Документация PCRE описывает, как избежать ошибки сегментации, ограничивая глубину рекурсии до безопасного значения, примерно равного размеру стека связанного приложения, деленного на 500. Когда глубина рекурсии должным образом ограничена, как рекомендовано, библиотека не генерирует переполнение стека и вместо этого изящно выходит с кодом ошибки. В PHP эта максимальная глубина рекурсии задается с помощью переменной конфигурации pcre.recursion_limit и (к сожалению) значение по умолчанию равно 100 000. Это значение TOO BIG! Ниже приведена таблица безопасных значений 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 script, переполнение стека НЕ ​​происходит, а вместо этого генерирует значимый код ошибки. То есть, СЛЕДУЕТ генерировать код ошибки! (Но, к сожалению, из-за другой ошибки 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), то совпадение было либо неудачным, либо произошла ошибка.

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

<?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...");

?>

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 3

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

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

"s" на конце в основном означает обработку строки как одну строку.

Ответ 4

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

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

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