Apache / PHP на Windows вылетает с регулярным выражением

Я обнаружил, что следующий код PHP падает, когда он работает на сервере Windows Apache:

<?php

$test = "0,4,447,11329,316,415,142,5262,6001,9527,11259,11236,1140,9770,9177,9043,11090,268,5270,9907,9196,10226,".
        "9399,1476,9368,6929,1659,11074,10869,8774,739,11344,10482,6399,8910,8997,11198,137,10148,10371,5128,767,2483,".
        "211,9973,10726,9299,778,11157,9497,275,9418,11141,241,5288,11324,776,10960,9289,7944,222,10686,11428,9723,".
        "10615,11399,9869,9083,10180,10043,9957,9387,9215,8869,9667,10174,10902,6607,9282,1259,395,10411,152,9344,8949,".
        "10923,8976,11042,11519,10704,10979,216,10044,9201,1721,5831,881,9721,1757,11054,1335,6151,9526,9081,111,498,".
        "2960,438,5313,206,318,10820,8192,6039,9161,11012,1717,1360,10757,4314,11280,9647,9346,10546,11006,9553,10365,".
        "6148,10565,4532,2776,4124,8853,6145,478,4539,540,9981,726,7186,11122,324,10524,1139,7900,9581,6869,1724,10851,".
        "10059,10018,11032,1290,3818,782,796,917,8740,6935,11439,10799,10948,249,2068,8778,6289,295,2766,9425,791,309,".
        "4753,10418,771,260,10835,10441,6434,10164,10475,10842,9013,11224,2247,8972,2141,2078,2152,475,9077,6291,10285,".
        "8067,753,6660,10889,431,2503,6007,9180,810,11447,2461,3689,7104,10150,10921,895,10598,747,10570,305,4497,11055,".
        "11496,10938,10722,8761,10086,11482,6780,6685,6918,10286,10659,9996,4074,9118,907,5192,283,2230,8884,6966,".
        "8820,8132,3598,9599,6796,11257,7049,5992,8637,4168,9017,7950,7165,10721,10037,1071,8044,759,11429,6380,".
        "10239,1593,9455,9704,10357,6737,2958,4051,9754,6564,11407,8716,7485,1528,6857,7406,9579,7259,1609,7820,".
        "4448,10289,1123,7005,8123,9316,914,9655,5280,9710,7822,510,10795,10476,8706,6160,8248,6978,9300,10643,".
        "7106,10250,519,7860,4733,904,8773,4714,8695,8633,6105,3312,11548,9580,10389,4886,4587,513,8485,4606,".
        "6471,581,526,637,3523,3772,3153,9336,9120,7633,3755,10087,524,10015,8563,556,1230,570,3652,569"
        .",8473,10209,3886,573,5363,4715,3865,9452,1218,7066,575,577,4724,7655"
        ;

$hest = preg_match('/^\d+(?:,\d+)+$/', $test);
var_dump($hest);
?>

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

Однако, поскольку код представлен здесь, код вызывает сбой Apache и респаун - ошибки не регистрируются. Если я пытаюсь запустить код напрямую через PHP, он работает как положено, поэтому проблема как-то связана с Apache.

Я экспериментировал с параметром "pcre.recursion_limit" и обнаружил, что если я уменьшу его до 689, он не умрет, но вместо этого вызов preg_match() завершится неудачно с PREG_RECURSION_LIMIT_ERROR. Для более высоких значений pcre.recursion_limit Apache умирает.

Apache 2.2.11 PHP 5.3.0

Я также проверил это на сервере Debian, где я НЕ вижу ошибки, и на разных версиях PHP и Apache для Windows, где ошибка возникает, как описано выше, поэтому кажется, что это может быть связано с Windows.

Кто-нибудь видел это раньше? Мне бы очень хотелось какой-нибудь намек!

ОБНОВЛЕНИЕ: Я из-за "ошибки" в Apache для Windows - ну, на самом деле, это не ошибка, но кажется, что Apache скомпилирован с небольшим размером стека, и это вызывает эту ошибку в PCRE, когда PHP его использует. Смотрите http://bugs.php.net/bug.php?id=47689

3 ответа

Решение

Это всегда хорошая идея уменьшить "pcre.recursion_limit", потому что высокое значение по умолчанию может повредить стек процесса (см. http://php.net/manual/en/pcre.configuration.php) - это именно то, что происходит с ваш mod_php установить. Поскольку функции preg не выдают ошибку при достижении пределов рекурсии / обратного отслеживания, может быть полезно иметь обертку наподобие

function match($re, $text) {
    preg_match($re, $text, $m);
    if(preg_last_error())
         trigger_error("preg: " . preg_last_error());
    return $m;
 }

По крайней мере, это дает вам знать, когда что-то идет не так.

Кроме того, попробуйте упростить ваши шаблоны, когда это возможно, например, /^\d[\d,]*\d$/ делает то же, что и выше, но с нулевой рекурсией.

Увеличьте стек Apache, и проблема исчезнет, ​​или используйте конфигурацию httpd для ее увеличения.

Большинство сборок Apache имеют слишком маленький размер стека, в то время как у нас есть нормальная сборка для PHP. Это объясняет, почему одни и те же выражения будут работать в CLI, а не в Apache (стек зависит от процесса, а процесс принадлежит / определен Apache).

Большое спасибо за этот пост. Это решило мою проблему.

Конфиг для стека Apache на Windows является причиной. Быстрый способ увеличить размер стека Apache - использовать директиву ThreadStackSize в конфигурации Apache ( http://httpd.apache.org/docs/2.2/mod/mpm_common.html).

Это может выглядеть так:

<IfModule mpm_winnt_module>
   ThreadStackSize 8*1024*1024
</IfModule>

Он устанавливает размер стека в 8 МБ, так же, как значение по умолчанию в Linux.

(Спасибо за https://bugs.php.net/bug.php?id=47689)

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

Другие вопросы по тегам