Использование JSON в web приложениях. Обработка данных PHP скрипта.

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

В предыдущей статье я обещал показать пример использования JSON в реальном web приложении. А обещания, как известно, нужно выполнять, тем более такие простые 🙂 . К тому же, уже написано очень подходящее для этого примера web приложение.

Приложение, о котором идет речь, используется для тестирования регулярных выражений. Подробно о его структуре и работе я писал в статье «Тестирование регулярных выражений». Точнее, это был цикл из трех статей (часть 1, часть 2, часть 3).

Сейчас я только кратко напомню его принцип работы. Приложение состоит из четырех файлов.
index.html – страница с формой.
scripts.js – скрипт с функцией, которая выполняет ajax запрос.
prototype.js – файл с библиотекой prototype (используется для упрощения работы с JavaScript).
analyzer.php – серверная часть приложения (php скрипт), выполняет поиск в тексте по заданному регулярному выражению и возвращает результат, который вставляется в index.html между тегами <div id=”results”>…</div>.

На данный момент результат работы скрипта (analyzer.php) представляет собой строку, содержащую кроме результатов поиска еще и html разметку, т.е. полностью подготовленную к вставке в страницу.

Такой подход имеет свои преимущества. Например, максимально сокращается количество JavaScript кода. Но, если мы захотим изменить дизайн приложения или порядок вывода результатов, то придется изменять серверную часть (analyzer.php). Теперь представьте, что мы имеем несколько страниц (например, на разных языках), которые должны использовать analyzer.php. Представили? Думаю, никому не понравится идея поддерживать несколько версий одного и того же скрипта.

Чтобы избавиться от всех этих недостатков, мы перенесем всю работу, касающуюся разметки страницы в клиентскую часть приложения, а серверная часть будет возвращать только результаты обработки текста.
При этом чтобы сохранить «чистоту эксперимента» обеспечим неизменный внешний вид приложения, т.е. пользователь не должен заметить отличий новой версии от предыдущей.

Передавать данные будем в формате JSON, т.к. существуют удобные функции для преобразования объектов PHP и JS в него и обратно.

Заранее результаты работы скрипта неизвестны, поэтому мы будем записывать результаты поиска в ассоциативный массив. Рассмотрим этот массив подробнее. Он содержит следующие поля:

$results['matches_num'] //количество найденных совпадений
$results['matches'] //массив с результатами поиска
$results['err_message'] //текст сообщения об ошибке
$results['counter_value'] //текущее значение счетчика

Теперь посмотрим, как изменится серверная часть приложения (analyzer.php).

$t = null;
$r = null;
$c = null;
//создаем массив с результатами поиска
$results = array();
if (isset($_POST['exp']) && isset($_POST['text'])
		&& isset($_POST['textcase']) && ($_POST['exp'] != '')
		&& ($_POST['text'] != '')) {
	$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) {
		$results['matches_num'] = $n;
		$results['matches'] = $res;
	}
	else {
		$results['matches_num'] = 0;
	}
}
else {
	$results['err_message'] = "Нужно задать регулярное выражение и текст для поиска";
}
//считаем количество вызовов скрипта
if (!file_exists('counter.txt')) {
	touch('counter.txt');
	$fh = fopen('counter.txt', 'r+');
	fwrite($fh, '1');
	fclose($fh);
	$results['counter_value'] = 1;
}
else {
	$fh = fopen('counter.txt', 'r+');
	$counter = fgets($fh);
	$counter += 1;
	rewind($fh);
	fwrite($fh, (string)$counter);
	fclose($fh);
	$results['counter_value'] = $counter;
}
echo json_encode($results);

Если вы сравните этот файл с его предыдущей версией, то заметите, что вместо отправки отформатированных результатов (строки, начинающиеся с echo …), мы заполняем данными наш массив. В последней строке мы преобразовываем массив в формат JSON и отправляем клиентской части (scripts.js).

В целом, количество кода в analyzer.php сократилось за счет отсутствия html разметки. Также сократилась длина передаваемой строки.

Теперь перейдем к клиентской части нашего приложения.
Сраница с формой (index.html) остается без изменений. Здесь я ее приводить не буду, смотрите статью «Тестирование регулярных выражений (клиентская часть)».

А вот в файле scripts.js произошли существенные изменения.

function analyseText() {
    //читаем значения введенные в поля
    var e = $('regexp').value;
    var t = $('searchText').value;
    var c = $('case').checked;
    //формируем строку с параметрами запроса
    var pars = $H({exp:e, text:t, textcase:c}).toQueryString();
    //отправляем ajax запрос
    new Ajax.Request("analyser.php",
            {method:"post", parameters:pars, onSuccess:parseResponse});
}

function parseResponse(transport) {
    var data = eval('(' + transport.responseText + ')');
    if (data.err_message == null) {
        if (data.matches_num > 0) {
            $('results').innerHTML = "<span class="bold">Найдено совпадений: "
                    + data.matches_num + "</span><br />";
            data.matches.each(
                function(value, index) {
                    if (index != 0) {
                        $('results').innerHTML += "Совпадения с подмаской "
                            + index + "<br />";
                    }
                    for (j = 0; j < value.length; j++) {
                        $('results').innerHTML += value[j] + "<br />";
                    }
                }
            );
        }
        else {
            $('results').innerHTML = "Совпадения не найдены";
        }
    }
    else {
        $('results').innerHTML = data.err_message;
    }
    $('results').innerHTML += "<p><small>Количество вызовов программы: "
        + data.counter_value + "</p></small>";
}

Прежде всего, обратите внимание на функцию analyseText(). В предыдущей версии отправка ajax запроса выполнялась так:

new Ajax.Updater("results", "analyser.php", {method:"post", parameters:pars});

Этот метод нам не подходит, т.к. он вставляет в страницу результат без обработки. Поэтому в этот раз мы используем:

new Ajax.Request("analyser.php", {method:"post", parameters:pars, onSuccess:parseResponse});

Запрос отправляется точно также, но результат передается функции, указанной в параметре onSuccess, т.е. parseResponse.
Примечание: здесь используются методы класса Ajax из библиотеки Prototype.

Эта функция (строки 13-40) преобразует строку JSON в объект JavaScript (строка 14), форматирует результат и вставляет его в блок <div id=”results”>.
После преобразования строки в объект JavaScript, мы можем получить доступ к данным, используя свойства этого объекта. Названия свойств совпадают с именами ключей в ассоциативном массиве, сформированном в analyzer.php.

Разберем подробнее работу этой функции. В строке 15 мы проверяем свойство err_message, если оно не установлено, то продолжаем обработку, в противном случае выводим сообщение об ошибке (строка 36).

После этого проверяем количество совпадений (поле matches_num). Если совпадения найдены, то выводим содержимое массива, который находится в свойстве matches (строки 19-29). Для вывода массива мы используем возможности, предоставляемые библиотекой prototype, в данном случае метод each(). В этом методе мы определили анонимную функцию (строки 20-28), которая вызывается для каждого элемента массива.
Т.к. каждый элемент массива matches содержит результаты поиска для одной из подмасок регулярного выражения, то для вывода результатов мы используем вложенный цикл (строки 25-27).

Вы, наверно, заметили, что при вставке содержимого массивов в страницу используется функция $('results'). Это функция входит в состав prototype и её работа аналогична document.getElementById('results').

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

Вы можете скачать архив с файлами приложения.

Постовой

Не хотите мерзнуть? Используйте современный теплоизоляционный материал