Тест PHP-функции preg_match

    Если PHP-функции preg_match в качестве исходной строки передать строку состоящую из 100000 (ста тысяч) или более силволов, то она вернёт неверный результат. Точнее результатом в этом случае всегда будет false.

    Проверяется очень просто.
    $max_n=100010;
    $n=99900;
    $tmp_str=str_repeat('b',$n);
    $go=true;
    while (($go)&&($n<=$max_n)) {
    $n++;
    $tmp_str.='b';
    $str='a'.$tmp_str.'a';
    $go=preg_match('/a.*?a/sim', $str);
    if (!$go) {
    echo 'error on length '.strlen($str);
    die;
    }
    }
    echo ' ok';

    Результатом работы данного скрипта будет такое сообщение
    error on length 100000

    Что говорит о том, что preg_match не осилил строку длинной сто тысяч символов. При более длинных строках результат будет тот же. Хотя мне не до конца понятно почему функция спотыкается именно на строке длинной в 100000 байт. Видимо это особенность реализации.

    Проверял на PHP Version 5.2.6-1.

    Поэтому рекомендую воздержаться от использования функции preg_match в случае, когда ей возможно придётся обрабатывать большие строки.

    Всё выше сказанное также справедливо и для PHP-функции preg_match_all.

    UPD Решение проблемы из коментария l2k
    ini_set(«pcre.backtrack_limit»,10000000);
    Таким образом можно изменить лимит обратных ссылок PCRE.
    Причём изменяемость директивы PHP_INI_ALL, что означает что она может быть установлена в любом месте.
    Share post

    Comments 42

    • UFO just landed and posted this here
        0
        Я парсил темплейты. Размеры были более 150 кб.
          +5
          А я делал парсеры к сайтам на ASP.NET и к его ajax. Так там аякс ответы бывают по 250-350 кбайт, причем 40 кбайт из них — hidden поле VIEWSTATE которое хранит состояние контролов и тягается постоянно по всем страницам!
          Отличный ajax! :)
        +3
        «Блямс!», сказала бензопила — «А-а-а, блин!», сказали мужики.
          +6
          … «А-а-а, блин!», сказали уже не мужики
          +1
          Надо же, незнал. Спасибо.
            +3
            Я тоже не знал. Но когда из-за этой ошибки не работал проект, то потратил не мало времени и сил, чтобы обнаружить это. Конечно, как всегда «никто ничего не трогал». Но особенно удручало, что «всё должно работать».

            Поэтому теперь помню, что не все функции работают так как ожидается разработчиком.
              0
              Поведайте же нам. Что вы передавали туда? :)
                0
                html страницы наверно?
                  –5
                  Размах хороший, но стоит по немного проверять, а то так можно и длину string переплюнуть :)
                    0
                    А как же unit-тесты?
                      0
                      Веселое решение. Давайте теперь на каждую функцию php будем вешать unit test!
                        0
                        Ну писать на каждую функцию юнит-тест действительно бред. Писать их надо на логически завершенные части проекта, как мне кажется. Тогда бы автор не потратил много времени и сил на поиск неисправности. Локализовать было бы легче.
                          +1
                          Дело в том, что даже при наличии тестов такие баги будут жить очень долго.
                          Модуль не прошел тест? — ищем ошибки в коде. До посинения.
                          Локализировали проблему — пишем локальный подтест. Причем нет гарантии, что тест будет проверять данные объемом 100кб (кто мог подумать...). Тест проходит успешно. И снова пошли за миноискателем.
                          Тесты — не панацея.
                            0
                            Да, действительно. По крайней мере я бы не стал писать проверочные данные объёмом больше 100КБ. Действительно кто бы мог подумать. Спасибо.
                              +1
                              сразу, возможно — не стали бы (хотя практики подразумевают тестирование как рядовых входных данных, так и пограничных).
                              но после появления такой проблемы зафиксировать её в тестах просто категорически необходимо.
                      0
                      А какова она? Разве не пока выделенная память закончится?
                        0
                        Что за длина string?
                          0
                          Это был сарказм.
                +21
                ini_set(«pcre.backtrack_limit»,10000000);
                вас спасёт
                  +1
                  Мне кажется, или за 100 кб html-кода в одном файле надо бить не файл, а разработчика?
                    +1
                    почему это ещё? А как же например парсинг какой-нибудь выгрузки БД?

                    Да и 100кб html — тоже нормальная цифра для каталогов или ещё чего-нибудь
                      +3
                      БД лучше стараться парсить средствами БД.
                        +1
                        Спорю что-ли?)

                        Я про реальные случаи, с которыми приходится сталкиваться.
                      +1
                      недавно хотел страницу в yahoo.pipes пропустить.
                      яха меня послала. страница 500 кб, а допустимо <=200 кб.

                      так и подумал, что убивать надо таких.
                      там новости одной лентой за 3 года.
                      ни RSS, ни страниц.
                        0
                        Вам наверное стоит спросить здесь.

                        А использовать парсер можно, например для подсветки C++/Java/PHP программ.
                      0
                      как это обьяснить?:)

                      1. Test failed
                      2. Test passed!
                      3. Test passed!
                      4. Test passed!
                      5. Test passed!
                      6. Test passed!
                      7. <?
                      8. $i      =   200000;
                      9. $x      =   "b".str_repeat("a",$i)."b";
                      10. if(preg_match("/^b.*?b$/",$x)){
                      11.     echo "Test passed!<br>";
                      12. }
                      13. else{
                      14.     echo "Test failed<br>";
                      15. }
                      16.  
                      17. $i      =   200;
                      18. $x      =   "b".str_repeat("a",$i)."b";
                      19.  
                      20. if(preg_match("/^b.*?b$/",$x)){
                      21.     echo "Test passed!<br>";
                      22. }
                      23. else{
                      24.     echo "Test failed<br>";
                      25. }
                      26.  
                      27. $i      =   200000;
                      28. $x      =   "b".str_repeat("a",$i)."b";
                      29. if(preg_match("/^ba*b$/",$x)){
                      30.     echo "Test passed!<br>";
                      31. }
                      32. else{
                      33.     echo "Test failed<br>";
                      34. }
                      35.  
                      36. $i      =   200;
                      37. $x      =   "b".str_repeat("a",$i)."b";
                      38.  
                      39. if(preg_match("/^ba*b$/",$x)){
                      40.     echo "Test passed!<br>";
                      41. }
                      42. else{
                      43.     echo "Test failed<br>";
                      44. }
                      45.  
                      46.  
                      47. $i      =   200000;
                      48. $x      =   "b".str_repeat("a",$i)."b";
                      49. if(preg_match("/^b.*b$/",$x)){
                      50.     echo "Test passed!<br>";
                      51. }
                      52. else{
                      53.     echo "Test failed<br>";
                      54. }
                      55.  
                      56. $i      =   200;
                      57. $x      =   "b".str_repeat("a",$i)."b";
                      58.  
                      59. if(preg_match("/^b.*b$/",$x)){
                      60.     echo "Test passed!<br>";
                      61. }
                      62. else{
                      63.     echo "Test failed<br>";
                      64. }
                      65.  
                      66. highlight_file(__FILE__);
                      67.  
                      68.  
                      69. ?>
                      70.  
                        0
                        Test passed!
                        Test passed!
                        Test passed!
                        Test passed!
                        Test passed!
                        Test passed!
                        <?
                        ini_set("pcre.backtrack_limit",10000000);
                          0
                          Уже прочитал, пасип) Дефолт-лимит — 100 000
                          0
                          foreach (array(200000, 200, 200000, 200) as $i) {

                          }
                          0
                          Для парсинга есть еще такие замечательные вещи, как phpQuery ) правда, в код я не заглядывал, если честно.
                            0
                            * и поэтому не могу сказать, как там дела обстоят с preg'ами. ereg'ами )
                            +3
                            Во-первых, как вам уже указали выше, это лечимо.

                            Во-вторых, проблема не в длине строке, а в самом выражении. Точнее в глубине рекурсии.

                            В своё время нарвался на эту проблему, пришлось переписывать шаблоны.
                              +1
                              php.ini:

                              [Pcre]
                              pcre.recursion_limit=700000
                              pcre.backtrack_limit=700000

                              Уже сталкавался с таким, мне пока для шаблонов хватило столько :)
                                +1
                                Вообще лучше не применять preg_match и подобные ей веши для прсинга больших данных.
                                Для больщих данных лучше что-то специфическое или свое решение.
                                А так для провекри правельности введенных данных с форм к примеру вполне но не более.
                                  0
                                  Вот я помучался с такой штукой, когда писал для себя один скрипт.
                                    0
                                    Сталкивался с подобной проблемой, но вероятно не в этом было дело, задача была собирать ссылки на внутренние страницы, сайты были больших размеров, на серваке после сбора 1000 ссылок, выходила 502 ошибка, но на локалхосте все работало, подскажите в чем могла быть проблема?
                                      +3
                                      Не стоит также забывать о других ограничениях описанных в www.pcre.org/pcre.txt раздел LIMITATIONS
                                        0
                                        За такую регулярку на такой длине надо наказывать. Сильно.

                                        /a[^a]a/
                                          0
                                          Собственно, я хотел сказать, что pcre.backtrack_limit кагбе говорит, что проблема не в php или pcre, а в вашей регулярке.
                                          Я не могу представить регулярку, для которой нужно менять этот лимит.
                                          +3
                                          заголовок провокационный: это не тест конкретной функции пхп, а тест pcre, который один для всех.
                                            0
                                            Угу, и я на него купился

                                          Only users with full accounts can post comments. Log in, please.