Приватный чат онлайн без регистрации php javascript. Пишем чат на рнр

25.10.2021

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

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

PHP Flat Visual ChatЭтот уникальный чат для организации онлайн консультанта , помимо своего основного назначения, может служить своеобразным гидом по Вашему сайту. В процессе общения Вы можете визуально выделить тот или иной элемент на странице сайта и показать его пользователю. Этот автономный продукт имеет собственную независимую панель для управления аккаунтами операторов.
Стоимость: $16

ShoutCloudShouldCloud - гибкий и обладающий богатыми возможностями PHP/AJAX чат , который исключительно легко интегрируется в сайт в течение нескольких минут. Он не требует MySQL базы данных, а нужные для работы файлы создает автоматически. Графический интерфейс автоматически под размеры контейнера, в котором размещается чат.
Стоимость: $8

Chat Plus ProChat Plus PRO - мощный PHP/AJAX скрипт для чата с адаптивным интерфейсом. С этим замечательным продуктом Ваши посетители проведут не одну бессонную ночь в неторопливых беседах. В сообщениях допускается использовать изображения, ссылки на видео в youtube или другие сайты. Поддерживаются профайлы пользователей, в которые они могут помещать различную личную информацию. Возможно создание отдельных «комнат» по интересам и размещение их на разных сайтах.
Стоимость: $24

Social Meet ScriptSocial Meet Script выполнен в так называемом tinder-стиле - приложения для поиска знакомств и общения. Открыв чат Вы сможете выбрать понравившегося Вам пользователя и, если он ответит Вам взаимностью, начать общение. Social Meet Script имеет встроенный чат, систему геолокации, а также VIP-членство.
Стоимость: $23

PHP Chat with WebClientEngage Visitor Chat - скрипт онлайн консультанта с широкими возможностями настройки. Простой и удобный интерфейс администратора. Примечательной особенностью этого чата является наличие Windows-клиента - Вы сможете видеть приходящие сообщения в области уведомлений на Рабочем столе и общаться с клиентами непосредственно из Windows, даже не заходя в административную панель сайта.
Стоимость: $32

Quick PHP ChatQuick PHP Chat - легкий в использовании скрипт для организации чата на Вашем сайте. Имеет 10 встроенных цветовых схем, позволяющих настроить внешний вид в соответствии с цветовой гаммой Вашего сайта. Административные функции позволяют управлять аккаунтами пользователей, в частности «банить» отдельных пользователей, а также сообщать им причину «бана».
Стоимость: $7

PHP Support Center and Live ChatPHP Support Center - система управления техподдержкой в минималистическом стиле. Ориентирован на организацию службы техподдержки, позволяет закреплять операторов и администраторов за отдельными компаниями, заказчиками, отделами и пользователями. Существует возможность использования редактора mailchip для придания Вашим спискам рассылки уникального вида.
Стоимость: $32

PHP Live Support ChatPHP Live Chat - автономное приложение онлайн консультанта , которое может быть встроено в любой сайт. В отличие от сервисов, предоставляющих услуги чата, Вы получаете полную свободу в его использовании. Никаких абонплат - заплатили один раз и пользуетесь сколько угодно!
Стоимость: $19

Boom ChatBoomchat имеет дружелюбный пользовательский интерфейс и ряд уникальных особенностей. Полностью интерфейс обеспечивает удобство использования на любых устройствах, будь то компьютер, планшет или смартфон.
Стоимость: $18

ReadyChatНовый продукт от разработчика DesignSkate. Значительно усовершенствованный по сравнению с предыдущей разработкой «moChat»: более оптимизированный и приятный на вид как для пользователя, так и для администратора.
Стоимость: $15

Simple Chat ScriptПростой и легкий SimpleChat можно интегрировать в любую php-страницу. Имеет HTML

Как обычно первый шаг посвящен разметке HTML. Наш документ строится в соответствии с HTML5, что позволяет использовать новый, более короткий синтаксис DOCTYPE , и опускать атрибут type в тегах script .

index.html

Делаем AJAX веб чат с использованием PHP, MySQL и jQuery | Демонстрация для сайта сайт Делаем AJAX веб чат с использованием PHP и jQuery

Для оптимизации загрузки, стили включены в секции head , а файлы JavaScript подключаются внизу документа, перед закрывающим тегом body .

Для организации прокручиваемой области со строками чата мы используем плагин jScrollPane . Данный плагин имеет свои собственные стили, который включаются в секции head .

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

Затем мы включаем файлы JavaScript: библиотеку jQuery, плагин mousewheel (используется в jScrollPane), плагин jScrollPane и наш файл script.js .


Схема базы данных

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

Для нашего скрипта мы используем две таблицы. В таблице webchat_users хранится информация об участниках чата. Таблица имеет поля id , name , gravatar и last_activity . Поле name определено как уникальное, таким образом предотвращается использование дублирующихся имен в чате.


Другим полезным свойством поля с уникальным индексом является то, что запрос на вставку данных завершится с ошибкой и свойство inserted_rows объекта MySQLi будет установлено в значение 0, если попытаться вставить дублирующиеся строки. В классе PHP Chat данное свойство будет активно использоваться.

Поле last_activity содержит значение времени. Значение обновляется каждые 15 секунд для каждого пользователя. Поле также определено как индекс, что позволяет быстро удалять неактивных пользователей (значение в поле last_activity более 15 означает, что пользователь более не просматривает окно чата).

Таблица webchat_lines содержит записи в чате. Заметьте, что мы храним имя автора и gravatar здесь тоже. Такое дублирование позволяет нам отказаться от использования затратной директивы join при запросе последних записей - наиболее часто используемых в нашем приложении.


Определения таблиц имеются в файле tables.sql в исходниках. Вы можете использовать текст запросов для создания таблиц. Также, при установке чата на свой хост, нужно поменять установки в ajax.php на ваши данные для соединения с базой MySQL.

PHP

Теперь у нас есть база данных, давайте обсудим скрипт PHP, который управляет чатом.

Первый файл, который мы рассмотрим, ajax.php . Он обрабатывает запросы AJAX от клиентской части из jQuery и выводит данные в формате JSON.

ajax.php

/* Конфигурация базы данных. Добавьте свои данные */ $dbOptions = array("db_host" => "", "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

Class 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 - Часть 1

/* Класс 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 - Часть 2

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); } }

jQuery отправляет запросы getUsers() каждые 15 секунд. Мы используем данный факт, чтобы удалить реплики, которые старше 5 минут и неактивных пользователей из базы данных. Потенциально можно было бы удалять данные записи в getChats , но этот запрос поступает каждую секунду и дополнительная нагрузка может повлиять на производительность приложения.

В методе getChats() используется функция gmdate вывода времени в формате GMT. В клиентской части мы используем значения часов и минут для установки в объекте JavaScript, а в результате время отображается в соответствии с часовым поясом пользователя.

Продолжение во второй части!

Живое общение на сайте не оставит равнодушным посетителя, ведь всегда можно написать в чат, и тебе что-то да и ответят, на сайтах все реже можно встретить данную функцию, разработчики стали забывать о ней, ссылаясь более на форумы и комментарии. Но если есть возможность поставить чат на сайте, то не нужно терять такую возможность, пользователи будут только благодарны. В данном уроке мы рассмотрим как создать весьма интересный чат для сайта, с использованием сервисов аватаров, что придаст общению более приятной формы. Для того чтобы создать такой AJAX чат мы будем использовать PHP, MySQL и jQuery. Для начала мы рассмотрим разметку HTML, документ которого строится в соответствии с HTML5, что позволяет использовать новый, более короткий синтаксис DOCTYPE, и опускать атрибут type в тегах script.
index.html

Как создать чат для сайта с помощью PHP | Демонстрация для сайта RUDEBOX

Для организации прокручиваемой области со строками чата мы используем плагин jScrollPane. Данный плагин имеет свои собственные стили, который включаются в секции head.
Разметка чата состоит из четырех основных элементов div – верхней панели, контейнера чата, контейнера пользователей и нижней панели. Последний div содержит формы для регистрации пользователя и отправки сообщения. Форма отправки сообщения по умолчанию скрыта и выводится, только если пользователь успешно вошел в систему чата.
Примечание! Определения таблиц имеются в файле tables.sql в исходниках. Вы можете использовать текст запросов для создания таблиц. Также, при установке чата на свой хост, нужно поменять установки в ajax.php на ваши данные для соединения с базой MySQL.
Когда Вы создали таблицу и сделали все, что сказано в примечании то давайте обсудим скрипт PHP, который управляет чатом. Первый файл, который мы рассмотрим, ajax.php. Он обрабатывает запросы AJAX от клиентской части из jQuery и выводит данные в формате JSON.
ajax.php

/* Конфигурация базы данных. Добавьте свои данные */ $dbOptions = array("db_host" => "", "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