Пишем чат на php. Пишем чат на рнр. Что для этого нужно

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

Ключевая особенность чата в том, что его содержимое обновляется автоматически . Отсюда и возникают все трудности.

Для начала разберём с Вами структуру таблицы в базе данных . Вот те поля, которые обязательно потребуются:

  • id - уникальный идентификатор.
  • name - имя, оставившего сообщение. Здесь также может быть, например, user_id , означающий id пользователя из другой таблицы.
  • message - сам текст сообщения.
  • date - дата отправления сообщения.

Безусловно, это только пример. Вы можете добавлять ещё массу других полей, тем самым, расширяя возможности Вашего чата.

Теперь необходимо вывести HTML-код :













Имя Сообщение Дата
Имя Сообщение Дата




В данном коде опять же всё как пример. Можно всё смело менять, но принцип должен быть тем же: есть место, где выводятся сообщения, причём они выводятся в одинаковом виде. Вместе с полем, где выводятся сообщения, есть текстовое поле и кнопка "Отправить ".

Обязательно для блока chat поставьте фиксированную высоту, чтобы при добавлении новых сообщений, у Вас этот блок не вырастал, также поставьте полосу прокрутки у него, чтобы можно было просмотреть все сообщения. Всё это делается с помощью CSS .

Теперь займёмся JavaScript :


function send() {
var message = document.getElementById("message").value;
var name = "Гость";
/* Здесь блок отправки POST-запроса с данными (например, через Ajax) */
}
function update() {
/* Здесь отправка запроса на получение всех сообщений (например, через Ajax) */
/* Здесь вывод сообщений в определённом формате прямо в HTML-код с использованием DOM, либо JQuery. */
}
setInterval("update()", 1000); // Обновление окна чата каждую секунду

И, наконец, PHP-код (добавление новых сообщений):

И последний файл, которые потребуется - это получение всех сообщений из таблицы:

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

План

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

PHP – будет управлять всеми сторонними материалами приложения
- Запись новых сообщений в текстовый файл
- Чтение новых сообщений из текстового файла
- Запрос состояния текстового файла
- Основная безопасность

jQuery/javascript – управление клиентской стороной приложения. Данное приложение разработано AJAX, что означает, что сообщения будут появляться во всплывающем окне (как ваши, так и собеседников), без требований перезагрузки страницы.
- Периодический запрос сервера на наличие новых сообщений
- Добавление новых сообщений в окно чата
- Прокрутка окна чата к новым сообщениям
- Запрос и установка имени пользователя
- Ограничение размера сообщений во избежание огромных сочинений в окне чата
- Основная безопасность

Текстовый файл – хранит историю чата

Отойдем от плана

В данной статье мы охватим различные интересные вещи и технологии, и в конечном счете, получим чат. Правда, такой чат будет сложно назвать самым лучшим чатом с самым большим количеством возможностей. Вы не сможете кикать или банить людей. Несколько человек могут ставить себе одинаковое имя. Вы не сможете видеть всех активных участников чата. Другими словами, это вам не IRC. Это просто интересное и забавное демо, которое даже можно применить в нескольких ситуациях. Если у вас есть желание в дальнейшем улучшить ваш чат и добавить много различных функций, то мы бы советовали вам следить за материалами нашего сайта!

Основная структура HTML

jQuery/PHP Chat


Your message:




Глядите, немного драгоценной разметки. На самом деле, 50% того, на что вы смотрите, не имеет никакого отношения с направленностью данной статьи. Оформление страницы центрирует вещи. Двойные div’ы, примененные к разметке чата и окна сообщений используются лишь для прибавления (не очень-то и нужного) эффекта двойной границы окна сообщений.

Самыми важными областями являются textarea с идентификатором «sendie», и div окна сообщений. На этом будет указывать javascript.

Часть движка на javascript

Мы собираемся добиться немного объектного ориентирования посредством javascript. Мы создадим функцию Чата, которая будет верхнем звеном в связке с остальными функциями, связанными с вещами, относящимися к чату.

Function Chat () {
this.update = updateChat;
this.send = sendChat;
this.getState = getStateOfChat;
}
UpdateChat будет давать запрос серверу о наличии новых линий в текстовом файле. Если же они присутствуют, то функция опубликует их в окне чата. sendChat будет задействована, когда сообщение уже будет напечатано и будет нажата кнопка отправки сообщения. Функция пропустит данные на сервер для дальнейшей обработки. getStateOfChat опрашивает сервер о количестве линий в текстовом файле, что позволяет сравнить данные и определить, есть ли новые строки или нет. Данная информация возвращается также как и JSON. А функции выглядят так:

//gets the state of the chat
function getStateOfChat(){
if(!instanse){
instanse = true;
$.ajax({
type: "POST",
url: "process.php",
data: {
"function": "getState",
"file": file
},
dataType: "json",
success: function(data){
state = data.state;
instanse = false;
},
});
}
}

//Updates the chat
function updateChat(){
if(!instanse){
instanse = true;
$.ajax({
type: "POST",
url: "process.php",
data: {
"function": "update",
"state": state,
"file": file
},
dataType: "json",
success: function(data){
if(data.text){
for (var i = 0; i < data.text.length; i++) {
$("#chat-area").append($("

"+ data.text[i] +"

"));
}
}
document.getElementById("chat-area").scrollTop = document.getElementById("chat-area").scrollHeight;
instanse = false;
state = data.state;
},
});
}
else {
setTimeout(updateChat, 1500);
}
}

//send the message
function sendChat(message, nickname)
{
updateChat();
$.ajax({
type: "POST",
url: "process.php",
data: {
"function": "send",
"message": message,
"nickname": nickname,
"file": file
},
dataType: "json",
success: function(data){
updateChat();
},
});
}
Все эти три функции используют возможности AJAX в jQuery, а также связывают их с PHP-файлом, который называется process.php. Его нам, конечно же, нам потребуется создать.

Часть движка на PHP

Часть информации, которая передается с AJAX, являются произвольным значением, и называется «function». Это просто дает файлу PHP информацию о том, какие вещи требуется выполнить. Первым делом мы выставим значение и настроим переключатель оператора, который будет охватывать все возможные функции. Мы также установим чистый массив, в котором будет храниться значения, которые будут кодироваться в JSON, а затем передаваться обратно.

Когда мы пытаемся активировать getState, будет читаться текстовый файл и просчитываться число строк. После того, как мы обновляем страницу, файл полностью прочтен, и новые линии возвращаются обратно. Когда мы отправляет сообщение, то оно записывается в текстовый файл, как новая линия.

$function = $_POST["function"];

$log = array();

Switch($function) {

Case("getState"):
$lines = file("chat.txt");
}
$log["state"] = count($lines);
break;

Case("update"):
$state = $_POST["state"];
if (file_exists("chat.txt")) {
$lines = file("chat.txt");
}
$count = count($lines);
if ($state == $count){
$log["state"] = $state;
$log["text"] = false;
} else {
$text= array();
$log["state"] = $state + count($lines) - $state;
foreach ($lines as $line_num => $line) {
if ($line_num >= $state){
$text = $line = str_replace("\n", "", $line);
}
}
$log["text"] = $text;
}

Case("send"):
$nickname = htmlentities(strip_tags($_POST["nickname"]));
$reg_exUrl = "/(http|https|ftp|ftps)\:\/\/+\.{2,3}(\/\S*)?/";
$message = htmlentities(strip_tags($_POST["message"]));
if (($message) != "\n") {
if (preg_match($reg_exUrl, $message, $url)) {
$message = preg_replace($reg_exUrl, "".$url."", $message);
}
fwrite(fopen("chat.txt", "a"), "". $nickname . "" . $message = str_replace("\n", " ", $message) . "\n");
}
break;
}
echo json_encode($log);
?>
Возьмемся за дело

Нам потребуется написать немного javascript, иначе праздник так и не начнется. Загрузите jQuery, подгрузите наш «движок», затем выполните несколько действий для того чтобы собрать несколько имен участников чата, чтобы получился разговор.

По ходу дела, давайте настроим ограничитель длины сообщений и присвоим функцию отправки сообщений кнопке return.



// ask user for name with popup prompt
var name = prompt("Enter your chat name:", "Guest");

// default name is "Guest"
if (!name || name === " ") {
name = "Guest";
}

// strip tags
name = name.replace(/(]+)>)/ig,"");

// display name on page
$("#name-area").html("You are: " + name + "");

// kick off chat
var chat = new Chat();

Chat.getState();

// watch textarea for key presses
$("#sendie").keydown(function(event) {

Var key = event.which;

//all keys including return.
if (key >= 33) {


var length = this.value.length;

// don"t allow new content if length is maxed out
if (length >= maxLength) {
event.preventDefault();
}
}
});
// watch textarea for release of key press
$("#sendie").keyup(function(e) {

If (e.keyCode == 13) {

Var text = $(this).val();
var maxLength = $(this).attr("maxlength");
var length = text.length;

// send
if (length "", "db_user" => "", "db_pass" => "", "db_name" => ""); /* Конец секции конфигурации базы данных */ error_reporting(E_ALL ^ E_NOTICE); require "classes/DB.class.php"; require "classes/Chat.class.php"; require "classes/ChatBase.class.php"; require "classes/ChatLine.class.php"; require "classes/ChatUser.class.php"; session_name("webchat"); session_start(); if(get_magic_quotes_gpc()){ // Удаляем лишние слэши array_walk_recursive($_GET,create_function("&$v,$k","$v = stripslashes($v);")); array_walk_recursive($_POST,create_function("&$v,$k","$v = stripslashes($v);")); } try{ // Соединение с базой данных DB::init($dbOptions); $response = array(); // Обработка поддерживаемых действий: switch($_GET["action"]){ case "login": $response = Chat::login($_POST["name"],$_POST["email"]); break; case "checkLogged": $response = Chat::checkLogged(); break; case "logout": $response = Chat::logout(); break; case "submitChat": $response = Chat::submitChat($_POST["chatText"]); break; case "getUsers": $response = Chat::getUsers(); break; case "getChats": $response = Chat::getChats($_GET["lastID"]); break; default: throw new Exception("Wrong action"); } echo json_encode($response); } catch(Exception $e){ die(json_encode(array("error" => $e->getMessage()))); }

Для удобства используется оператор switch для определения действий, которые обрабатывает скрипт. Здесь реализованы подсистемы чата, функциональность входа/выхода и действия по запросу списка реплик и пользователей в режиме онлайн.
Вывод осуществляется в форме сообщений JSON (которые удобно обрабатывать с помощью jQuery), ошибки генерируют исключения. Оператор switch распределяет все запросы соответствующим статическим методам класса Chat, который будет обсуждаться позже в данном разделе.
DB.class.php

DB { private static $instance; private $MySQLi; private function __construct(array $dbOptions){ $this->MySQLi = @ new mysqli($dbOptions["db_host"], $dbOptions["db_user"], $dbOptions["db_pass"], $dbOptions["db_name"]); if (mysqli_connect_errno()) { throw new Exception("Ошибка базы данных."); } $this->MySQLi->set_charset("utf8"); } public static function init(array $dbOptions){ if(self::$instance instanceof self){ return false; } self::$instance = new self($dbOptions); } public static function getMySQLiObject(){ return self::$instance->MySQLi; } public static function query($q){ return self::$instance->MySQLi->query($q); } public static function esc($str){ return self::$instance->MySQLi->real_escape_string(htmlspecialchars($str)); } }

Класс DB - менеджер базы данных. Конструктор объявлен как private, таким образом, объект не может быть создан вне пределов класса, и инициализация возможна только из статического метода init(). Он берет массив с параметрами соединения с MySQL и создает экземпляр класса, который содержится в статической переменной self::$instance. Таким образом, обеспечивается существование единственного соединения с базой данных в конкретный момент времени.
Остальная часть класса реализует коммуникацию с базой данных, в основе которой лежит статический метод query().
ChatBase.class.php

/* Базовый класс, который используется классами ChatLine и ChatUser */ class ChatBase{ // Данный конструктор используется всеми класса чата: public function __construct(array $options){ foreach($options as $k=>$v){ if(isset($this->$k)){ $this->$k = $v; } } } }

Это простой базовый класс. Его основное назначение - определить конструктор, который получает массив параметров, но сохраняет только те, которые определены в классе.
ChatLine.class.php

/* Строка чата */ class ChatLine extends ChatBase{ protected $text = "", $author = "", $gravatar = ""; public function save(){ DB::query(" INSERT INTO webchat_lines (author, gravatar, text) VALUES ("".DB::esc($this->author)."", "".DB::esc($this->gravatar)."", "".DB::esc($this->text)."")"); // Возвращаем объект MySQLi класса DB return DB::getMySQLiObject(); } }

Класс ChatLine является производным классом от ChatBase. Объект данного класса может быть легко создан с помощью передачи конструктору массива с текстом, именем автора и элементом gravatar. Свойство класса gravatar содержит хэш md5 email адреса. Оно нужно для получения пользовательского аватара, соответствующего email адресу, с сайта gravatar.com.
Данный класс также определяет метод save, который сохраняет объект в базе данных. Так как метод возвращает объект MySQLi, содержащийся в классе DB, вы можете проверить успешность завершения операции с помощью свойства affected_rows.
ChatUser.class.php

Class ChatUser extends ChatBase{ protected $name = "", $gravatar = ""; public function save(){ DB::query(" INSERT INTO webchat_users (name, gravatar) VALUES ("".DB::esc($this->name)."", "".DB::esc($this->gravatar)."")"); return DB::getMySQLiObject(); } public function update(){ DB::query(" INSERT INTO webchat_users (name, gravatar) VALUES ("".DB::esc($this->name)."", "".DB::esc($this->gravatar)."") ON DUPLICATE KEY UPDATE last_activity = NOW()"); } }

Класс имеет свойства name и gravatar (обратите внимание на модификатор доступа protected – свойства доступны в классе ChatBase, и мы можем устанавливать их значения в конструкторе).
В классе определен метод update(), который обновляет поле last_activity значением текущего времени. Таким образом показывается, что пользователь держит окно с чатом отрытым и его надо учитывать как автора в режиме онлайн.
Chat.class.php

/* Класс Chat содержит публичные статические методы, которые используются в ajax.php */ class Chat{ public static function login($name,$email){ if(!$name || !$email){ throw new Exception("Заполните все необходимые поля."); } if(!filter_input(INPUT_POST,"email",FILTER_VALIDATE_EMAIL)){ throw new Exception("Неправильный адрес email."); } // Подготовка кэша gravatar: $gravatar = md5(strtolower(trim($email))); $user = new ChatUser(array("name" => $name, "gravatar" => $gravatar)); // Метод save возвращает объект MySQLi if($user->save()->affected_rows != 1){ throw new Exception("Данное имя используется."); } $_SESSION["user"] = array("name" => $name, "gravatar" => $gravatar); return array("status" => 1, "name" => $name, "gravatar" => Chat::gravatarFromHash($gravatar)); } public static function checkLogged(){ $response = array("logged" => false); if($_SESSION["user"]["name"]){ $response["logged"] = true; $response["loggedAs"] = array("name" => $_SESSION["user"]["name"], "gravatar" => Chat::gravatarFromHash($_SESSION["user"]["gravatar"])); } return $response; } public static function logout(){ DB::query("DELETE FROM webchat_users WHERE name = "".DB::esc($_SESSION["user"]["name"])."""); $_SESSION = array(); unset($_SESSION); return array("status" => 1); }

Этот код выполняет всю работу. В операторе switch в файле ajax.php выбирались действия, которые соответствовали методам данного класса. Каждый из этих методов возвращает массив, который затем конвертируется в объект JSON с помощью функции json_encode() (это происходит внизу в файле ajax.php).
Когда пользователь входит в систему, его имя и gravatar сохраняются как элементы массива$_SESSION и становятся доступны в последующих запросах.
Chat.class.php

Public static function submitChat($chatText){ if(!$_SESSION["user"]){ throw new Exception("Вы вышли из чата"); } if(!$chatText){ throw new Exception("Вы не ввели сообщение."); } $chat = new ChatLine(array("author" => $_SESSION["user"]["name"], "gravatar" => $_SESSION["user"]["gravatar"], "text" => $chatText)); // Метод save возвращает объект MySQLi $insertID = $chat->save()->insert_id; return array("status" => 1, "insertID" => $insertID); } public static function getUsers(){ if($_SESSION["user"]["name"]){ $user = new ChatUser(array("name" => $_SESSION["user"]["name"])); $user->update(); } // Удаляем записи чата старше 5 минут и пользователей, неактивных в течении 30 секунд DB::query("DELETE FROM webchat_lines WHERE ts < SUBTIME(NOW(),"0:5:0")"); DB::query("DELETE FROM webchat_users WHERE last_activity < SUBTIME(NOW(),"0:0:30")"); $result = DB::query("SELECT * FROM webchat_users ORDER BY name ASC LIMIT 18"); $users = array(); while($user = $result->fetch_object()){ $user->gravatar = Chat::gravatarFromHash($user->gravatar,30); $users = $user; } return array("users" => $users, "total" => DB::query("SELECT COUNT(*) as cnt FROM webchat_users")->fetch_object()->cnt); } public static function getChats($lastID){ $lastID = (int)$lastID; $result = DB::query("SELECT * FROM webchat_lines WHERE id > ".$lastID." ORDER BY id ASC"); $chats = array(); while($chat = $result->fetch_object()){ // Возвращаем время создания сообщения в формате GMT (UTC): $chat->time = array("hours" => gmdate("H",strtotime($chat->ts)), "minutes" => gmdate("i",strtotime($chat->ts))); $chat->gravatar = Chat::gravatarFromHash($chat->gravatar); $chats = $chat; } return array("chats" => $chats); } public static function gravatarFromHash($hash, $size=23){ return "http://www.gravatar.com/avatar/".$hash."?size=".$size."&default=". urlencode("http://www.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?size=".$size); } }

В методе getChats() используется функция gmdate вывода времени в формате GMT. В клиентской части мы используем значения часов и минут для установки в объекте JavaScript, а в результате время отображается в соответствии с часовым поясом пользователя.
Стили чата содержатся в файле chat.css. Стили не зависят от остальной страницы и их легко встроить в уже существующий сайт. Нужно только включить разметку HTML, стили и файлы JavaScript.
chat.css

/* Основной контейнер чата */ #chatContainer{ width:510px; margin:100px auto; position:relative; } /* Верхняя панель */ #chatTopBar{ height:40px; background:url("../img/solid_gray.jpg") repeat-x #d0d0d0; border:1px solid #fff; margin-bottom:15px; position:relative; color:#777; text-shadow:1px 1px 0 #FFFFFF; } #chatTopBar .name{ position:absolute; top:10px; left:40px; } #chatTopBar img{ left:9px; position:absolute; top:8px; } /* Чат */ #chatLineHolder{ height:360px; width:350px; margin-bottom:20px; } .chat{ background:url("../img/chat_line_bg.jpg") repeat-x #d5d5d5; min-height:24px; padding:6px; border:1px solid #FFFFFF; padding:8px 6px 4px 37px; position:relative; margin:0 10px 10px 0; } .chat:last-child{ margin-bottom:0; } .chat span{ color:#777777; text-shadow:1px 1px 0 #FFFFFF; font-size:12px; } .chat .text{ color:#444444; display:inline-block; font-size:15px; overflow:hidden; vertical-align:top; width:190px; } .chat .gravatar{ background:url("http://www.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?size=23") no-repeat; left:7px; position:absolute; top:7px; } .chat img{ display:block; visibility:hidden; } .chat .time{ position:absolute; right:10px; top:12px; font-size:11px; } .chat .author{ margin-right:6px; font-size:11px; }

Начинается все с задания стилей для div #chatContainer. Он центрируется горизонтально с помощью свойства margin:100px auto; . Данный div разделяется на верхнюю панель, область чата, область пользователей и нижнюю панель.
Верхняя панель выводит информацию о зарегистрированном пользователе. Она получает относительное позиционирование, так что аватар, имя и кнопка "выйти" располагаются соответственно.
Затем следует div, который содержит все строки чата – #chatLineHolder. Данный div имеет фиксированную высоту и ширину, а в части данного урока, которая посвящена jQuery, мы используем плагин jScrollPane для превращения его в область с прокруткой контента с боковым слайдером.
chat.css

/* Область пользователя */ #chatUsers{ background-color:#202020; border:1px solid #111111; height:360px; position:absolute; right:0; top:56px; width:150px; } #chatUsers .user{ background:url("http://www.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?size=30") no-repeat 1px 1px #444444; border:1px solid #111111; float:left; height:32px; margin:10px 0 0 10px; width:32px; } #chatUsers .user img{ border:1px solid #444444; display:block; visibility:hidden; } /* Нижняя панель */ #chatBottomBar{ background:url("../img/solid_gray.jpg") repeat-x #d0d0d0; position:relative; padding:10px; border:1px solid #fff; } #chatBottomBar .tip{ position:absolute; width:0; height:0; border:10px solid transparent; border-bottom-color:#eeeeee; top:-20px; left:20px; } #chatContainer input{ background:url("../img/input_bg.jpg") repeat-x #dcdcdc; height:26px; font:13px/26px Calibri,Arial,sans-serif; color:#777; border:1px solid; border-color:#c1c1c1 #eee #eee #c1c1c1; text-shadow:1px 1px 0 #E4E4E4; padding:0 5px; margin-right:5px; width:185px; outline:none; } #submitForm{ display:none; }

Во второй части файла стилей мы оформляем контейнер #chatUsers и элементы div для пользователя. Каждый активный пользователь чата представлен изображением gravatar размером 32 на 32 пикселя. Изображение по умолчанию используется в качестве фона, и когда реальное изображение загружается с сервера gravatar.com, оно выводится сверху. Так предотвращается раздражающее мерцание, которое обычно появляется в момент загрузки изображения.
Остальная часть кода связана с нижней панелью и формой отправки. Элемент div .tipстановится треугольником, сделанным с использованием CSS, за счет установки нулевой высоты и ширины, при этом ширина обводки устанавливается большой.
chat.css

/* Изменение стилей по умолчанию для jScrollPane */ .jspVerticalBar{ background:none; width:20px; } .jspTrack{ background-color:#202020; border:1px solid #111111; width:3px; right:-10px; } .jspDrag { background:url("../img/slider.png") no-repeat; width:20px; left:-9px; height:20px !important; margin-top:-5px; } .jspDrag:hover{ background-position:left bottom; } /* Дополнительные стили */ a.logoutButton{ background-color:#bbb; border:1px solid #eee !important; color:#FFFFFF !important; font-size:12px; padding:5px 9px; position:absolute; right:10px; text-shadow:1px 1px 0 #888; top:7px; -moz-box-shadow:0 0 7px #888 inset; -webkit-box-shadow:0 0 7px #888 inset; box-shadow:0 0 7px #888 inset; } a.logoutButton:hover{ text-shadow:1px 1px 0 #888; -moz-box-shadow:0 0 7px #666 inset; -webkit-box-shadow:0 0 7px #666 inset; box-shadow:0 0 7px #666 inset; } #chatContainer .blueButton{ background:url("../img/button_blue.png") no-repeat; border:none !important; color:#516D7F !important; display:inline-block; font-size:13px; height:29px; text-align:center; text-shadow:1px 1px 0 rgba(255, 255, 255, 0.4); width:75px; margin:0; cursor:pointer; } #chatContainer .blueButton:hover{ background-position:left bottom; } p.noChats, #chatUsers .count{ clear:both; font-size:12px; padding:10px; text-align:center; text-shadow:1px 1px 0 #111111; } #chatUsers .count{ font-size:11px; } .rounded{ -moz-border-radius:4px; -webkit-border-radius:4px; border-radius:4px; } #chatErrorMessage{ width:100%; top:0; left:0; position:fixed; background-color:#ab0909; border-bottom:1px solid #d32a2a; font-size:23px; padding:16px; text-align:center; color:#fff; text-shadow:1px 1px 0 #940f0f; }

Перейдем к последней части нашего урока - коду jQuery. Чат работает, получая события от форм регистрации и отправки сообщения, а также от кнопки "Выйти", а также по расписанию отправляются запросы AJAX к серверу для проверки наличия новых сообщений и пользователей.
PHP часть обрабатывает запросы AJAX в файле ajax.php. jQuery генерирует следующие запросы AJAX:

  • Вход пользователя в систему: один запрос POST;
  • Выход пользователя из системы: один запрос POST;
  • Проверка пользователей, которые находятся в режиме онлайн: выполняется каждые 15 секунд;
  • Проверка новых записей: запрос GET генерируется каждую секунду. Такое функционирование может привести к очень высокой нагрузке на веб сервер, поэтому скрипт оптимизирован, и в зависимости от активности чата, период генерации запроса может быть увеличен до 15 секунд.
Мы определили пользовательские функции-обертки для функций AJAX jQuery $.get и $.post, которые помогают заполнять длинные параметры для генерации запроса.
Также весь код чата организован в один объект chat. Он содержит несколько удобных методов.
script.js

$(document).ready(function(){ // Запускаем метод init, когда документ будет готов: chat.init(); }); var chat = { // data содержит переменные для использования в классах: data: { lastID: 0, noActivity: 0 }, // Init привязывает обработчики событий и устанавливает таймеры: init: function(){ // Используем плагин jQuery defaultText, включенный внизу: $("#name").defaultText("Псевдоним"); $("#email").defaultText("Email (используется Gravatar)"); // Конвертируем div #chatLineHolder в jScrollPane, // сохраняем API плагина в chat.data: chat.data.jspAPI = $("#chatLineHolder").jScrollPane({ verticalDragMinHeight: 12, verticalDragMaxHeight: 12 }).data("jsp"); // Используем переменную working для предотвращения // множественных отправок формы: var working = false; // Регистрируем персону в чате: $("#loginForm").submit(function(){ if(working) return false; working = true; // Используем нашу функцию tzPOST // (определяется внизу): $.tzPOST("login",$(this).serialize(),function(r){ working = false; if(r.error){ chat.displayError(r.error); } else chat.login(r.name,r.gravatar); }); return false; });

Метод init() предназначен для привязки обработчиков событий к чату и запуску функций таймера, которые используются для проверки по расписанию наличия новых записей в чате и списка пользователей в режиме онлайн. Мы используем собственные функции-обертки –$.tzGET и $.tzPOST. Они принимают на себя всю тяжесть работы по заданию длинного списка параметров для запросов AJAX.
script.js

// Отправляем данные новой строки чата: $("#submitForm").submit(function(){ var text = $("#chatText").val(); if(text.length == 0){ return false; } if(working) return false; working = true; // Генерируем временный ID для чата: var tempID = "t"+Math.round(Math.random()*1000000), params = { id: tempID, author: chat.data.name, gravatar: chat.data.gravatar, text: text.replace(//g,">") }; // Используем метод addChatLine, чтобы добавить чат на экран // немедленно, не ожидая завершения запроса AJAX: chat.addChatLine($.extend({},params)); // Используем метод tzPOST, чтобы отправить чат // через запрос POST AJAX: $.tzPOST("submitChat",$(this).serialize(),function(r){ working = false; $("#chatText").val(""); $("div.chat-"+tempID).remove(); params["id"] = r.insertID; chat.addChatLine($.extend({},params)); }); return false; }); // Отключаем пользователя: $("a.logoutButton").live("click",function(){ $("#chatTopBar > span").fadeOut(function(){ $(this).remove(); }); $("#submitForm").fadeOut(function(){ $("#loginForm").fadeIn(); }); $.tzPOST("logout"); return false; }); // Проверяем состояние подключения пользователя (обновление браузера) $.tzGET("checkLogged",function(r){ if(r.logged){ chat.login(r.loggedAs.name,r.loggedAs.gravatar); } }); // Самовыполняющиеся функции таймаута (function getChatsTimeoutFunction(){ chat.getChats(getChatsTimeoutFunction); })(); (function getUsersTimeoutFunction(){ chat.getUsers(getUsersTimeoutFunction); })(); },

Во второй части скрипта мы продолжаем привязку обработчиков событий. В функции отправки формы можно заметить, что когда пользователь создает новую запись в чате, создается временная строка, которая выводится немедленно в окно чата, без ожидания завершения запроса AJAX. Как только запись будет завершена, временная строка удаляется с экрана. Так пользователь получает ощущения, что чат работает молниеносно, в то время как реальная запись происходит в фоновом режиме.
В конце данного куска кода запускаются две именованные самовыполняющиеся функции. Данные функции будут передаваться как параметры методам chat.getChats() илиchat.getUsers() соответственно, таким образом, могут быть установлены дополнительные таймауты.
script.js

// Метод login скрывает данные регистрации пользователя // и выводит форму ввода сообщения login: function(name,gravatar){ chat.data.name = name; chat.data.gravatar = gravatar; $("#chatTopBar").html(chat.render("loginTopBar",chat.data)); $("#loginForm").fadeOut(function(){ $("#submitForm").fadeIn(); $("#chatText").focus(); }); }, // Метод render генерирует разметку HTML, // которая нужна для других методов: render: function(template,params){ var arr = ; switch(template){ case "loginTopBar": arr = [ "", "",params.name, "Выйти"]; break; case "chatLine": arr = [ "","",params.author, ":",params.text,"",params.time,""]; break; case "user": arr = [ "" ]; break; } // Единственный метод join для массива выполняется // быстрее, чем множественные слияния строк return arr.join(""); },

В данной части кода внимания требует метод render(). Он собирает шаблон в зависимости от параметра template. Метод затем создает и возвращает запрашиваемый код HTML, встраивая в него значения второго параметра объекта params.
script.js

// Метод addChatLine добавляет строку чата на страницу addChatLine: function(params){ // Все показания времени выводятся в формате временного пояса пользователя var d = new Date(); if(params.time) { // PHP возвращает время в формате UTC (GMT). Мы используем его для формирования объекта date // и дальнейшего вывода в формате временного пояса пользователя. // JavaScript конвертирует его для нас. d.setUTCHours(params.time.hours,params.time.minutes); } params.time = (d.getHours() < 10 ? "0" : "") + d.getHours()+":"+ (d.getMinutes() < 10 ? "0":"") + d.getMinutes(); var markup = chat.render("chatLine",params), exists = $("#chatLineHolder .chat-"+params.id); if(exists.length){ exists.remove(); } if(!chat.data.lastID){ // Если это первая запись в чате, удаляем // параграф с сообщением о том, что еще ничего не написано: $("#chatLineHolder p").remove(); } // Если это не временная строка чата: if(params.id.toString().charAt(0) != "t"){ var previous = $("#chatLineHolder .chat-"+(+params.id - 1)); if(previous.length){ previous.after(markup); } else chat.data.jspAPI.getContentPane().append(markup); } else chat.data.jspAPI.getContentPane().append(markup); // Так как мы добавили новый контент, нужно // снова инициализировать плагин jScrollPane: chat.data.jspAPI.reinitialise(); chat.data.jspAPI.scrollToBottom(true); },

Метод addChat() получает в качестве параметра объект, который содержит строку чата, имя автора и gravatar, и вставляет строку чата в соответствующее место в контейнере div#chatContainer. Каждая строка чата (если она не является временной) имеет уникальный ID, который назначается MySQL. Данный id как имя класса для строки чата в формате chat-123.
Когда метод addChat() выполняется, он проверяет существование предыдущей строки (дляchat-123 будет проверяться наличие chat-122). Если она существует, метод вставляет новую строку после нее. Если нет, то просто добавляет новую строку к div. Такая простая техника управляет вставкой строк в правильном порядке и поддерживает его на протяжении всей работы чата.
script.js

// Данный метод запрашивает последнюю запись в чате // (начиная с lastID), и добавляет ее на страницу. getChats: function(callback){ $.tzGET("getChats",{lastID: chat.data.lastID},function(r){ for(var i=0;i 3){ nextRequest = 2000; } if(chat.data.noActivity > 10){ nextRequest = 5000; } // 15 секунд if(chat.data.noActivity > 20){ nextRequest = 15000; } setTimeout(callback,nextRequest); }); }, // Запрос списка всех пользователей. getUsers: function(callback){ $.tzGET("getUsers",function(r){ var users = ; for(var i=0; i< r.users.length;i++){ if(r.users[i]){ users.push(chat.render("user",r.users[i])); } } var message = ""; if(r.total