Добро пожаловать!
Здесь вы можете найти ответ на интересующий вас вопрос в отрасли сайтостроения, познакомится ближе с web технологиями и web стандартами.

Статьи

Обзор AJAX-библиотек для PHP с практическими примерами

Как известно, писать приложения в идеологии AJAX сложно по многим причинам, начиная довольно трудоемкой отладкой и заканчивая разработкой функций для рутинных действий. А зачем это делать заново, ведь есть готовые библиотеки для PHP!? В этой статье я расскажу о том, как разрабатывать AJAX-приложения, не сходя при этом с ума.

Ищем подопытных

Для этого обзора я выбрал 3 довольно сильно различающихся по функционалу библиотеки. Почему только 3? Дело в том, что другие интересные AJAX-решения обычно являются частью полноценных фреймворков для создания сайтов, поэтому их обзор занял бы слишком много места. Стало быть, о них – в следующий раз, а пока мы познакомимся с участниками сегодняшнего состязания.

Итак, внимание на сцену! Первым выступает представитель легкой весовой категории – Sajax, за ним уверенно двигается крепкий середнячок – Xajax, и, наконец, могучий тяжеловес, жонглер гирями, способный удержать на своей груди платформу с роялем, оркестром и взводом королевских мушкетеров, – Projax.

А что делать-то будем?

Сколько «пустых» статей и других материалов об AJAX и втором поколении веб-технологии публикуются ежедневно? Трудно сосчитать :). В них популярно и доказательно обосновывается, что все это очень круто и прогрессивно, но при этом авторы забывают рассказать нам одну малость – как всего этого добиться. Чтобы не быть голословным, я покажу тебе все это на конкретных примерах, часть из которых ты сможешь сразу использовать на своих веб-сайтах. Начнем с самой простой библиотеки – Sajax.

Sajax

Sajax – довольно простая библиотека, поэтому серверный и клиентский код мы можем (и будем :)) писать в одном файле. Функционал у нашей первой программы будет очень простым: мы нажимаем кнопку, и в текстовое поле загружается текст, разумеется, без перезагрузки страницы.

Для начала сделаем серверную часть, в которой будет экспортироваться функция, возвращающая текст для клиента:

<?php
require("Sajax.php");

function 
hello_world() {
  return 
"Hello, world!";
}

sajax_init();
// Раскомментировать для отладочного режима
// $sajax_debug_mode = 1;
sajax_export("hello_world");
sajax_handle_client_request();
?>

Последняя строка этого скрипта автоматически обрабатывает запросы клиента.

Ну что же, перейдем к клиентской части, которой надо выдать HTML и JavaScript (поместим мы ее в тот же файл, сразу за закрывающимся тэгом PHP).

Для автоматической генерации JavaScript мы воспользуемся библиотечной функцией sajax_show_javascript(), а для асинхронной работы JavaScript - определим функцию do_hello_world, которая будет вызываться при нажатии кнопки, и функцию do_hello_world_callback, которая сработает при получении ответа сервера и положит результат в текстовое поле.

Вся соль библиотеки Sajax кроется в работе do_hello_world, которая вызывает автоматически сгенерированную x_hello_world. Фактически, мы из JavaScript вызываем функцию, которая написана на PHP и хранится на сервере, а в качестве параметра передаем ей функцию обратного вызова:

<html>
<head>
<title>Hello, world! by sAJAX</title>
<script>
<?php sajax_show_javascript(); ?>
function do_hello_world_callback(result) {
  document.getElementById("return_string").value = result;
}
function do_hello_world() {
  x_hello_world(do_hello_world_callback);
}
</script>
</head>
<body>
<input type="text" id="return_string" value="Здесь будет результат запроса">
<input type="button" value="Послать запрос" onclick="do_hello_world(); return false;">
</body>
</html>

Запустим скрипт и посмотрим описание функции x_hello_world, которое автоматически генерируется при вызове PHP-функции sajax_export("hello_world"):

// оболочка для функции hello_world
function x_hello_world() {
   sajax_do_call("hello_world", x_hello_world.arguments);
}

В результате происходит прозрачный вызов PHP-функции, которая хранится на сервере. Теперь попробуем вызвать серверную функцию с параметрами, для чего напишем гостевую книгу, в которую сообщения будут добавляться без перезагрузки страницы, а отображение новых сообщений будет происходить автоматически. Для простоты я предположу, что у нас есть API для работы с сообщениями. Это может быть программный интерфейс к базе данных или XML-хранилищу. В нем нам нужны 3 функции:

  • messages_api_print_messages() – печать всех сообщений в виде HTML
  • messages_api_add_message($message) – добавление сообщения
  • messages_api_get_messages() – возврат всех сообщений в виде HTML

Теперь напишем серверную часть, в которой изменится только описание функции и ее экспорт:

<?php
require ("Sajax.php");
require (
"messages_api.php");

function 
add_message($message) {
  
messages_api_add_message($message);
  return 
messages_api_get_messages();
}
sajax_init();
sajax_export("add_message");
sajax_handle_client_request();
?>

На стороне клиента у нас будет поле для ввода текста и кнопка для отправления. Сообщения будут отображаться ниже:

<html>
<head>
<title>Гостевая книга by sAJAX</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script>
<?php sajax_show_javascript(); ?>
function do_add_message_callback(result) {
  document.getElementById("messages").innerHTML = unescape(result);
}
function do_add_message() {
  message = document.getElementById("message").value;
  x_add_message(message, do_add_message_callback);
}
</script>
</head>
<body>
<textarea id="message"></textarea><br />
<input type="button" value="Отправить" onclick="do_add_message(); return false;">
<hr />
<div id="messages">
<?php
messages_api_print_messages
();
?>
</div>
</body>
</html>

При нажатии на кнопку «Отправить» вызывается функция x_add_message, которой в качестве параметра передается не только функция обратного вызова, но и содержимое текстового поля. Надеюсь, внимательный читатель обратил внимание и на вызов функции unescape(result) для нормального отображения кириллических символов. Если нет желания каждый раз вызывать ее, можно пропатчить саму библиотеку.

Xajax

Несмотря на то что эта библиотека чуть мощнее предыдущей, работать с ней не намного сложнее. Чтобы быстро войти в курс дела, посмотрим, как можно с помощью Xajax загрузить файл по нажатию кнопки:

<?php
require ('xajax.inc.php');

function 
loadFile($file) {
  
$objResponse = new xajaxResponse();
  
$objResponse->addAssign("result""innerHTML"file_get_contents($file));
  
$objResponse->addAssign("result""style.visibility"'visible');
  return 
$objResponse;
}

$xajax = new xajax();
// Раскомментировать для отладки
//$xajax->debugOn();
$xajax->registerFunction("loadFile");
$xajax->processRequests();
?>
<html>
<head>
<title>Загрузка файла</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?php $xajax->printJavascript(''); ?>
</head>
<body style="text-align: center;">
<button onclick="xajax_loadFile('file.data')">Загрузить файл</button>
<div style="border: 1px solid black; visibility:hidden;" id="result"></div>
<br/>
</body>
</html>

Первое, что бросается в глаза при прочтении кода, - поддержка ООП в Xajax, ведь мы работаем не с функциями, а с классами и объектами, которые при желании можно расширить. Общая схема работы аналогична Sajax, по-другому идет разве что отправка ответа с сервера клиенту – для этого используется объект $objResponse класса xajaxResponse. Я использовал метод addAssign, который ищет объект HTML с заданным id и определенному полю присваивает нужное значение. То есть мы можем не только прозрачно вызывать серверные функции на клиенте, но и прозрачно модифицировать HTML с сервера (и добавлять JavaScript).

Кстати, помнится, я обещал продемонстрировать, как надо разделять AJAX-систему на клиента и сервер. Настало время исполнить обещание, а заодно – показать, как Xajax умеет автоматически отсылать формы на сервер.

На каждом сайте, который поддерживает создание сетевого сообщества, имеется процедура регистрации с помощью специальной формы. Чтобы зарегистрироваться, надо потратить немало сил: то пароль слишком легкий, то логин занят - проще просто бросить это дело. Попробуем сделать эту процедуру безболезненной, быстрой и интерактивной.

Логин, введенный пользователем, должен быть незанятым и отвечать некоторым требованиям, например состоять более чем из трех символов. На пароль мы наложим только ограничение по длине: 6 символов. Дополнительные проверки можешь придумать сам – все зависит от твоей фантазии ;). Вся система будет содержать 3 файла:

  • register.common.php – общий функционал для клиента и сервера
  • register.server.php – серверная часть для проверки логина, пароля и обработки формы
  • register.php – клиентская часть с интерактивной формой ввода

Начнем с общей части для клиента и сервера, в которой будут регистрироваться 3 функции для проверки логина, пароля и для приема данных с клиента. Поскольку серверная часть будет у нас в отдельном файле, это надо указать в конструкторе объекта $xajax:

<?php
require ('xajax.inc.php');
$xajax = new xajax("register.server.php");
// Раскомментировать для отладки
//$xajax->debugOn();
$xajax->registerFunction("checkUserName");
$xajax->registerFunction("checkUserPass");
$xajax->registerFunction("submitForm");
$xajax->processRequests();
?>

Функции для проверки я предлагаю описать отдельно, так как они будут вызываться у нас в двух случаях: когда форма будет передана на сервер и когда по мере набора текста пользователем будет происходить автоматическая проверка:

<?php
function isUserNameGood($userName) {
  
// Их надо брать из базы ;)
  
$users = array("bill""john""vasya");
  if (
strlen($userName) <= 3)
    return array(
false"Имя пользователя должно быть длиннее трех символов");
  foreach (
$users as $user) {
    if (
$user == $userName) return array(false"Имя пользователя уже используется");
  }
  return array(
true"Имя пользователя подходит");
}

function 
isUserPassGood($userPass) {
  
$objResponse = new xajaxResponse();
  if (
strlen($userPass) <= 6
    return array(
false"Пароль должен быть длиннее шести символов");
  return array(
true"Пароль подходит");
}
?>

Обе функции возвращают 2 значения, первое имеет булевский тип и определяет, подходят ли логин и пароль соответственно, а второе содержит пояснительный текст. Теперь реализовать функции, которые будут вызываться клиентом, не составит труда:

<?php
function checkUserName($userName) {
  
$objResponse = new xajaxResponse();
  list(
$isUserNameGood$message) = isUserNameGood($userName);
  
$objResponse->addAssign("userNameOk""innerHTML"$message);
  return 
$objResponse;
}
function 
checkUserPass($userPass) {
  
$objResponse = new xajaxResponse();
  list(
$isUserPassGood$message) = isUserPassGood($userPass);
  
$objResponse->addAssign("userPassOk""innerHTML"$message);
  return 
$objResponse;
}
?>

Отмечу, что результат у меня помещается в специальные ячейки в таблице с айдишниками - userNameOk и userPassOk, которые мы опишем на стороне клиента. И последнее, что нам осталось сделать на сервере, - это создать функцию, которая будет обрабатывать данные из формы для регистрации пользователя. Все содержимое передается этой функции в виде параметра, который представляет собой ассоциативный массив (аналог $_POST и $_GET):

<?php
function submitForm($formData) {
  
$objResponse = new xajaxResponse();
  list(
$isUserNameGood$message) = isUserNameGood($formData['userName']);
  
$objResponse->addAssign("resultDiv""innerHTML"$message);
  list(
$isUserPassGood$message) = isUserPassGood($formData['userPass']);
  
$objResponse->addAppend(”resultDiv”"innerHTML""<br/>" $message);
  if (
$isUserNameGood && $isUserPassGood)
    
$objResponse->addAppend("resultDiv""innerHTML""<br/><strong>Регистрация прошла успешно</strong>");
  else
    
$objResponse->addAppend("resultDiv""innerHTML""<br/><strong>Регистрация завершилась неудачей</strong>");
  return 
$objResponse;
}
?>

Обрати внимание, что в этих скриптах я нигде не использую базу данных или любое другое постоянное хранилище, чтобы код не потерял ясность. В реальных условиях при успешной регистрации пользователя обязательно надо сохранять его данные, да и получать список пользователей надо тоже из какого-то хранилища. Теперь напишем клиентский код, который будет описывать форму регистрации. Чтобы браузер отправил форму без перезагрузки страницы, нужно «обнулить» (точнее, «завойдить» :)) параметр action у формы и прописать событие onsubmit. При этом событии будет вызвана JavaScript-функция xajax_sumbitForm, которая является оберткой нашей серверной функции submitForm, а параметром будут как раз данные формы. Проверка данных формы во время их набора пользователем осуществляется при помощи вызова соответствующих функций по событию onkeyup:

<?php
require ('register.common.php');
?>
<html>
<head>
<title>Регистрация пользователя</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?php $xajax->printJavascript(''); ?>
<script type="text/javascript">
function submitForm() {
  xajax.$('submit').disabled=true;
  xajax.$('submit').value="Регистрируется пользователь...";
  xajax_submitForm(xajax.getFormValues("registrationForm"));
  return false;
}
</script>
</head>
<body>
<form id="registrationForm" action="javascript:void(null);" onsubmit="submitForm();">
<table border="1" width="50%">
<tr>
<td width="40%">Имя пользователя </td>
<td><input onkeyup="xajax_checkUserName(this.value)" type="text" id="userName" name="userName" /></td>
</tr>
<tr>
<td id="userNameOk" colspan="2"> </td>
</tr>
<tr>
<td width="40%">Пароль</td>
<td><input type="text" onkeyup="xajax_checkUserPass(this.value)" id="userPass" name="userPass" /></td>
</tr>
<tr>
<td id="userPassOk" colspan="2"> </td>
</tr>
<tr>
<td> </td>
<td><input type="submit" id="submit" value="Submit" /></td>
</tr>
</table>
<div id="resultDiv"> </div>
</form>
</body>
</html>

Projax

Настало время десерта – самой мощной библиотеки в нашем обзоре. Я покажу ее возможности на очень распространенном сейчас виджете – поле ввода с автозавершением (a-ля Google Suggest). Для этого у нас есть все необходимое, остается только подключить JavaScript-файлы и вызвать метод text_field_with_auto_complete – на этом клиентская часть заканчивается.

<?php
include ("projax.php");
$projax= new Projax();
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Демонстрация автозавершения by Projax</title>
<script src="js/prototype.js" type="text/javascript"></script>
<script src="js/scriptaculous.js" type="text/javascript"></script>
</head>
<body>
Введите имя пользователя :<br />
<?=$projax->text_field_with_auto_complete('name'null, array('url'=>'get_names.php'));?>
</body>
</html>

Серверная часть будет просто печатать список строк, в котором есть подстрока, набранная пользователем:

<?php
// Имена пользователей берутся из базы
$names = array('boris''vasya''bill');
foreach(
$names as $name)
  
$ret_val .= (strstr($name,$_POST['name'])) ? '<li>'.$name.'</li>':'';
echo 
'<ul>' $ret_val '</ul>';
?>

Заключение

В этой статье мы рассмотрели 3 библиотеки, которые позволяют быстро писать эффективные приложения в связке PHP и JavaScript, реализуя таким образом методологию AJAX. Каждая из этих библиотек имеет как плюсы, так и минусы, которые надо знать, чтобы выбрать нужную из них для конкретного проекта.

Sajax

Sajax (Simple Ajax Toolkit) – это простая библиотека для создания AJAX-приложений на PHP путем прозрачного вызова серверных функций с клиента. Общий вес клиентского и серверного кода составляет чуть меньше 9 Кб, что делает библиотеку незаменимой для небольших и быстрых приложений. Ее можно посоветовать использовать тем, кто собирается переделывать свое AJAX-приложение без использования фреймворков, поскольку Sajax - довольно низкоуровневая библиотека, не содержащая лишних наворотов. В комплект поставки входят фреймворки для следующих языков: ASP, Cold Fusion, Io, Lua, Perl, PHP, Python и Ruby. К минусам библиотеки можно отнести некорректную работу с кириллицей (правда, проблема довольно просто решается).

Xajax

Для 90% всех веб-проектов хватит библиотеки Xajax, которая обеспечивает полностью прозрачное взаимодействие клиента и сервера (причем в обе стороны). Размер клиентской части в сжатом виде составляет 15 Кб, что вполне приемлемо для библиотеки с таким функционалом. В дистрибутив входят примеры (в том числе XUL-приложение для Mozilla Firefox) и тесты для этой библиотеки. Кроме того, хочу отметить наличие в интернете большого количества документации и сообщества, которое сложилось вокруг этого проекта. По моему очень скромному мнению, эта библиотека является наиболее сбалансированной по параметрам «функционал» и «легкость использования». Выбор редакции, так сказать ;).

Projax

Projax представляет собой порт проекта Prototype, который был первоначально написан для Ruby on Rails и проекта script.aculo.us. Такой мощный функционал объясняет общий размер клиентской части - более 170 Кб кода JavaScript. За эти «деньги» мы получаем полную «корзину фруктов» - от манипуляций с DOM до визуальных эффектов. Projax идеально подходит для «суперинтерактивных» проектов, например, онлайн-игр, где нужен мощный функционал на стороне пользователя. Минусом опять же является некоторая сложность этой библиотеки, которую ты, конечно, не заметишь, если раньше работал с Prototype и script.aculo.us

Информация

Библиотеки обычно содержат отладочный режим, при включении которого выдаются сообщения о ходе выполнения программы.

Для эффективной отладки AJAX-приложений необходимо настроить отладку как серверного PHP-кода, так и клиентского JavaScript.

Для отладки JavaScript проще всего использовать расширения для Mozilla Firefox - JavaScript Debugger и FireBug.