PHP скрипт. Тестирование регулярных выражений (серверная часть).

Владимир | | Ajax, PHP.

В прошлых статьях мы начали разработку web приложения для тестирования регулярных выражений. На данный момент мы разобрались с функциями и структурой приложения, а также написали его клиентскую часть. Сегодня мы напишем серверную часть.

Заметьте, что для работы клиентской части нашего приложения, не важно на каком языке (Java, PHP, .NET и т.д.) написана серверная часть. Главное, чтобы скрипт правильно обрабатывал параметры запроса и возвращал результат. Мы используем PHP, т.к. на сегодняшний день это, пожалуй, самый доступный вариант (можно найти даже бесплатный хостинг, возможностей которого вполне хватит для нашего приложения).

Серверная часть нашего приложения состоит всего из одного файла (analyzer.php), который получает запрос с исходными данными для поиска (регулярным выражением и текстом) и возвращает результаты поиска. Давайте посмотрим, как работает скрипт.

<?php
    $t = null;
    $r = null;
    $c = null;
    if (isset($_POST['exp']) && isset($_POST['text'])
            && isset($_POST['textcase'])) {
        $t = $_POST['text'];
        $r = $_POST['exp'];
        $c = $_POST['textcase'];
        //убираем двойные слеши если они были добавлены автоматически
        $r = stripslashes($r);
        $t = stripslashes($t);
        //если не нужно учитывать регистр, преобразуем все в нижний регистр
        if ($c == "false") {
            $t = mb_convert_case($t, MB_CASE_LOWER, "UTF-8");
            $r = mb_convert_case($r, MB_CASE_LOWER, "UTF-8");
        }
        //проверяем, есть ли слеши
        $forwardSlash = "/^/.*/i";
        $backwardSlash = "/.*/.?$/i";
        if (preg_match($forwardSlash, $r) == FALSE) {
            $r = "/".$r;
        }
        if (preg_match($backwardSlash, $r) == FALSE) {
            $r = $r."/";
        }
        //ищем все совпадения текста заданному выражению
        $res = null;
        $n = FALSE;
        $n = preg_match_all($r, $t, $res);
        //если совпадения найдены, выводим результат
        if ($n != FALSE) {
            echo "<span class="bold">Найдено совпадений: ".$n."</span><br />";
            $i = 0;
            while (isset($res[$i])) {
                if ($i > 0) {
                    echo "Совпадения с подмаской $i<br />";
                }
                foreach ($res[$i] as $value) {
                    echo "'".$value."'"."<br />";
                }
                $i++;
            }
        }
        else {
            echo "Совпадения не найдены. Проверьте как составлено" .
                    " регулярное выражение.";
        }
    }
    else {
        echo "Нужно задать регулярное выражение и текст для поиска";
    }
    //считаем количество вызовов скрипта
    if (!file_exists('counter.txt')) {
        touch('counter.txt');
        $fh = fopen('counter.txt', 'r+');
        fwrite($fh, '1');
        fclose($fh);
        echo "<p><small>Количество вызовов программы: 1</small></p>";
    }
    else {
        $fh = fopen('counter.txt', 'r+');
        $counter = fgets($fh);
        $counter += 1;
        rewind($fh);
        fwrite($fh, (string)$counter);
        fclose($fh);
        echo "<p><small>Количество вызовов программы: $counter</small></p>";
    }
?>

В первую очередь мы проверяем заданы ли параметры в массиве $_POST (функция isset должна возвратить true) и сохраняем их в локальных переменных (строки 5-9). Если все параметры заданы начинаем обработку. Напомню, наш скрипт получает 3 параметра: регулярное выражение ($_POST[‘exp’]), текст ($_POST[‘text’]) и параметр, указывающий нужно ли учитывать регистр текста и регулярного выражения ($_POST[‘textcase’]). Сразу же хочу остановиться на третьем параметре. Дело в том, что синтаксис регулярных выражений предусматривает задание этого параметра (нужно в конце регулярного выражения поставить букву «i», например, так “/abc.*/i”), и все прекрасно работает до тех пор, пока вы будете пользоваться только латинскими символами. А если в тексте есть символы кириллицы, то параметр “i” никакого влияния не оказывает. Судя по всему функции библиотеки PCRE, которую мы используем, не могут изменить регистр символов кириллицы (во всяком случае, для кодировки UTF-8). Для того, чтобы обойти это ограничение мы добавили параметр textcase. Если он равен true, мы преобразовываем и текст, и регулярное выражение к нижнему регистру, с помощью функции mb_convert_case, которая корректно работает с символами кириллицы, и только после этого выполняем поиск (строка 30).

Перед поиском мы выполним еще одну операцию (строки 19-26). Проверим, не забыл ли пользователь поставить слеши (“/”) в начале и конце регулярного выражения. И если их нет, доставим их за него. Кстати, нужно помнить, что после закрывающего слеша может идти символ (например, «i»). Регулярное выражение для поиска открывающего слеша имеет вид «/^\/.*/i«, а для закрывающего – «/.*\/.?$/i«. Если какой-то из слешей отсутствует, мы вставляем его сами.

А дальше все просто. С помощью функции preg_match_all (строка 30) мы выполняем поиск. Если совпадения найдены, функция вернет число совпадений, а сами совпадения будут записаны в массив $res. Это двумерный массив, в котором в элементах [0][0..m] находятся строки, совпадающие со всем регулярным выражением, в элементах [1][0..m] – строки, совпадающие с первой подмаской и т.д. (Примечание: подмаска – часть регулярного выражения, взятая в скобки.)

После этого, мы проходим через весь массив и возвращаем браузеру результаты поиска (строки 32-44). Если соответствия не были найдены или возникли ошибки, сообщаем об этом браузеру (строки 45-52).

На завершение (строки с 54 по 69) мы добавим простенький счетчик. Это, конечно, не обязательно, но с его помощью мы сможем узнать количество вызовов скрипта. Значение счетчика храниться в файле counter.txt. Алгоритм работы очень простой. Если файла (counter.txt) не существует, мы создаем его и записываем в него 1. В противном случае, читаем значение из файла, увеличиваем значение на 1 и записываем его обратно.
В конце мы возвращаем строку с результатами браузеру.

Вот и все.

Посмотреть работающее приложение можно здесь. Вы также можете скачать исходники и запустить их на своем компьютере (нужен web сервер + PHP).

Постовой

Швейцарские часы — символ престижа.
Мы предлагаем вам купить часы Movado

  • Вообще-то для нормальной обработки кириллицы (и японицы и т.п.) лучше ставить модификатор u

    Сам долгое время долбался с подобной проблемой.

    • Да, вы правы. Но у меня была ситуация, когда с его помощью проблему решить не получилось. Сейчас уже врядли найду тот скрипт, скорее всего я сам что-то напутал. В общем, решил перестраховаться и явно изменить регистр символов 😉

  • Вообще-то для нормальной обработки кириллицы (и японицы и т.п.) лучше ставить модификатор u

    Сам долгое время долбался с подобной проблемой.

    • Да, вы правы. Но у меня была ситуация, когда с его помощью проблему решить не получилось. Сейчас уже врядли найду тот скрипт, скорее всего я сам что-то напутал. В общем, решил перестраховаться и явно изменить регистр символов 😉

  • Fixx-iv

    1. На денвере тупо не работает «Совпадения не найдены. Проверьте как составлено регулярное выражение.» . Даже со значениями по умолчанию.
    2. И пишет оно это вот так:
    Совпадения не найдены. Проверьте как составлено регулярное выражение.

    • С Денвером ничем помочь не могу.

    • Такой бред пишет, когда кодировка выходе в браузер не совпадает с кодировкой файла. Второе сделайте utf-8 и будет вам ЩазТье! =)

  • а вообще с кириллицей заморочек много. Нужно использовать модификатор /u и, желательно на всякий случай указывать локаль: setlocale(LC_ALL, «ru_RU.UTF-8»);