Chat с помощью mysql и 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 чат для сайта.

И так поехали писать чат на ajax php. Создаём файл chat.js , а так же сразу создадим файлы add.php и get_json.php , но о них позже. Создаём файл chat.php . Подключаем к нему jquery и chat.js . Создаём в файле chat.php поле с именем участника чата и поле с сообщением в чат, а так же кнопку с id btnSend для отправки сообщения. На кнопку с id btnSend в файле chat.js повешен обработчик событий клик. В див с id chat будут выводиться наши чат сообщения. Ниже показан листинг куска кода из файла chat.php . Не обращайте внимания на непонятные css классы, это классы из bootstrap.

Ваше имя для чата Ваше сообщение Отправить сообщение

В chat.js обрабатывем данные из полей ввода и методом post в формате json отправляем данные в add.php . Кусок кода из chat.js ниже.

$("#btnSend").click(function(elem){ //при клике получаем сообщения из полей с ид name и text var name = $("#name").val(); var text = $("#text").val(); //и методом POST в формате json отправляем их в add.php $.post("add.php", {name: name, text: text}, function(){ //поле text стираем(тоесть делаем пустым "") $("#text").attr("value", ""); }); });

В файле add.php мы получаем данные. Конечно их надо бы проверить после получения но это мы пока опустим. Добавляем к данным дату и записываем их в файл messages.txt в одну строчку с уникальным разделителем "_". Так же для того что бы файл не разросся до больших размеров, сделана защита от переполнения. При достижении в файле messages.txt строк сообщений более 100, все строки стираются кроме последних пяти. Они зписываются заново. Ниже листинг add.php .

Всё, наши все сообщения пишутся в файл. Теперь нам нужно сделать так, что бы эти сообщения выводились на экран. Для этого напишем в файле chat.js функцию chatRequest() . Эта функция обращается к файлу get_json.php и передаёт ему параметр __maxId . Параметр _maxId указывает сколько сообщений у нас есть на данный момент. По умолчанию в начале стоит 0. Соответственно для начала __maxId определяем как глобальную переменную.

Function chatRequest(){ // Отправка запроса методом POST. $.post("get_json.php", {maxId: _maxId}, chatResult, "json"); }

Так же в функции chatRequest() определено, что при удачном выполнении запроса данные ответа возвращаются в формате json и вызывается функция chatResult из файла chat.js . Но давайте сначала разберём файл get_json.php .

Файл get_json.php принимает данные, вычисляет строку, которая была не отображена на экране, делает из неё массив, упаковывает в формат json и отправляет в функцию chatResult(msgs) .

В продолжение темы:
Графика

Друзья, многие из вас используют iMessage в повседневной жизни, но даже не замечают этого! Естественно, я говорю про SMS — если сообщение адресату ушло синим цветом, то в...

Новые статьи
/
Популярные