Создаем чат для сайта: HTML, JS, PHP и AJAX

Когда обычных комментариев становится недостаточно, приходит время создать чат.

С помощью чата пользователи могут общаться друг с другом. Иногда он становится самой популярной частью сайта, если аудитория заинтересована в общении, а качество реализации чата не заставляет плакать.

Каким должен быть чат

Во главе угла — удобство для пользователей. Нужно позаботиться о том, чтобы чат соответствовал современным требованиям:

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

Каркас чата на HTML

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

<div class='chat'>

<div class='chat-messages'>

<div class='chat-messages__content' id='messages'>

Загрузка...

</div>

</div>

<div class='chat-input'>

<form method='post' id='chat-form'>

<input type='text' id='message-text' class='chat-form__input' placeholder='Введите сообщение'> <input type='submit' class='chat-form__submit' value='=>'>

</form>

</div>

</div>

Дальше задаются стили:

.chat {

border:1px solid #333;

margin:15px;

width:40%;

height:70%;

background:#555;

color:#fff;

}

.chat-messages {

min-height:93%;

max-height:93%;

overflow:auto;

}

.chat-messages__content {

padding:1px;

}

.chat__message {

border-left:3px solid #333;

margin-top:2px;

padding:2px;

}

.chat__message_black {

border-color:#000;

}

.chat__message_blue {

border-color:blue;

}

.chat__message_green {

border-color:green;

}

.chat__message_red {

border-color:red;

}

.chat-input {

min-height:6%;

}

input {

font-family:arial;

font-size:16px;

vertical-align:middle;

background:#333;

color:#fff;

border:0;

display:inline-block;

margin:1px;

height:30px;

}

.chat-form__input {

width:79%;

}

.chat-form__submit {

width:18%;

}

Отображаться этот чат будет так:

В первую очередь стоит уделить внимание главным функциям чата, а после доработать внешний вид.

Основная часть на JS + PHP

Сообщения будут отправляться и загружаться с помощью AJAX. На JavaScript пишутся функции работы с интерфейсом и связи с серверной частью, а на PHP — методы обработки полученных данных и взаимодействия с базой данных.

Начать стоит с создания переменных на JS:

var messages__container = document.getElementById('messages');

//Контейнер сообщений — скрипт будет добавлять в него сообщения

var interval = null; //Переменная с интервалом подгрузки сообщений

var sendForm = document.getElementById('chat-form'); //Форма отправки

var messageInput = document.getElementById('message-text'); //Инпут для текста сообщения

Теперь создадим функцию, через которую посылаются запросы. Она получает переменную act, в которой будет храниться одно из трех значений: auth (авторизация), load (загрузка) и send (отправка). От них зависит, какая информация будет передана в PHP-файл.

function send_request(act, login = null, password = null) {//Основная функция

//Переменные, которые будут отправляться

var var1 = null;

var var2 = null;

if(act == 'auth') {

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

var1 = login;

var2 = password;

} else if(act == 'send') {

//Если нужно отправить сообщение, то получаем текст из поля ввода

var1 = messageInput.value;

}

$.post('includes/chat.php',{ //Отправляем переменные

act: act,

var1: var1,

var2: var2

}).done(function (data) {

//Заносим в контейнер ответ от сервера

messages__container.innerHTML = data;

if(act == 'send') {

//Если нужно было отправить сообщение, очищаем поле ввода

messageInput.value = '';

}

});

}

Теперь можно создать функцию обновления и указать для нее интервал выполнения:

function update() {

send_request('load');

}

interval = setInterval(update,500);

После отлавливается событие отправки формы — это поможет отказаться от обновления страницы:

sendForm.onsubmit = function () {

send_request('send');

return false; //Возвращаем ложь, чтобы остановить классическую отправку формы

};

Теперь займемся самим обработчиком. В первую очередь с помощью функции session_start() запускается сессия, затем подключается база данных:

session_start();//Подключение должно быть на первой строчке в коде, иначе появится ошибка

$db = mysqli_connect("localhost","login","password");

mysqli_select_db($db,"chat");

//Заносим данные админа в сессию

$_SESSION['login'] = 'admin';

$_SESSION['password'] = 'admin';

$_SESSION['id'] = 1;

Напишем функцию авторизации:

function auth($db,$login,$pass) {

//Находим совпадение в базе данных

$result = mysqli_query($db,"SELECT * FROM userlist WHERE login='$login' AND password='$pass'");

if($result) {

if(mysqli_num_rows($result) == 1) {//Проверяем, одно ли совпадение

$user = mysqli_fetch_array($result); //Получаем данные пользователя и заносим их в сессию

$_SESSION['login'] = $login;

$_SESSION['password'] = $pass;

$_SESSION['id'] = $user['id'];

return true; //Возвращаем true, потому что авторизация успешна

} else {

unset($_SESSION); //Удаляем все данные из сессии и возвращаем false, если совпадений нет или их больше 1

return false;

}

} else {

return false; //Возвращаем ложь, если произошла ошибка

}

}

Дальше на очереди функция загрузки:

function load($db) {

$echo = "";

if(auth($db,$_SESSION['login'],$_SESSION['password'])) {//Проверяем успешность авторизации

$result = mysqli_query($db,"SELECT * FROM messages"); //Запрашиваем сообщения из базы

if($result) {

if(mysqli_num_rows($result) >= 1) {

while($array = mysqli_fetch_array($result)) {//Выводим их с помощью цикла

$user_result = mysqli_query($db,"SELECT * FROM userlist WHERE id='$array[user_id]'");//Получаем данные об авторе сообщения

if(mysqli_num_rows($user_result) == 1) {

$user = mysqli_fetch_array($user_result);

$echo .= "<div class='chat__message chat__message_$user[nick_color]'><b>$user[login]:</b> $array[message]</div>"; //Добавляем сообщения в переменную $echo

}

}

} else {

$echo = "Нет сообщений!";//В базе ноль записей

}

}

} else {

$echo = "Проблема авторизации";//Авторизация не удалась

}

return $echo;//Возвращаем результат работы функции

}

Функция отправки сообщения реализуется проще:

function send($db,$message) {

if(auth($db,$_SESSION['login'],$_SESSION['password'])) {//Если авторизация удачна

$message = htmlspecialchars($message);//Заменяем символы ‘<’ и ‘>’на ASCII-код

$message = trim($message); //Удаляем лишние пробелы

$message = addslashes($message); //Экранируем запрещенные символы

$result = mysqli_query($db,"INSERT INTO messages (user_id,message) VALUES ('$_SESSION[id]','$message')");//Заносим сообщение в базу данных

}

return load($db); //Вызываем функцию загрузки сообщений

}

В функции уже присутствует несложная валидация, но можно добавить и другие меры защиты от инъекций и спама:

  • проверку на дублирование прошлого сообщения;
  • удаление внешних ссылок;
  • цензуру мата;
  • премодерацию сообщений у некоторых групп пользователей и так далее.

Теперь, когда все функции готовы, пропишем их вызов:

//Получаем переменные из супермассива $_POST

//Тут же их можно проверить на наличие инъекций

if(isset($_POST['act'])) {$act = $_POST['act'];}

if(isset($_POST['var1'])) {$var1 = $_POST['var1'];}

if(isset($_POST['var2'])) {$var2 = $_POST['var2'];}

switch($_POST['act']) {//В зависимости от значения act вызываем разные функции

case 'load':

$echo = load($db); //Загружаем сообщения

break;

case 'send':

if(isset($var1)) {

$echo = send($db,$var1); //Отправляем сообщение

}

break;

case 'auth':

if(isset($var1) && isset($var2)) {//Авторизуемся

if(auth($db,$var1,$var2)) {

$echo = load($db);

}

}

break;

}

echo $echo;//Выводим результат работы кода

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

Теперь, когда чат работает, пора добавить авторизацию. Для этого можно создать отдельную форму во фронте, но можно обойтись и модальными окнами. В функции send_request() дополним отправку запроса:

$.post('includes/chat.php',{

act: act,

var1: var1,

var2: var2

}).done(function (data) {

messages__container.innerHTML = data;

if(data == 'Проблема авторизации') {

clearInterval(interval); //Если проблема авторизации, отключаем автообновление

if(login == null && password == null) {

login = prompt('Введите логин: ');//Запрашиваем логин

if(login != null) {

password = prompt('Введите пароль: ');//Запрашиваем пароль

send_request('auth',login,password); //Отправляем еще один запрос

}

}

}

if(act == 'auth') {

interval = setInterval(update,500); //Заново запускаем автообновление

}

if(act == 'send') {

messageInput.value = '';

}

});

Вот как это выглядит:

Дополнительные функции

Минимальный необходимый функционал есть, и чат можно запускать в релиз, но можно добавить еще несколько полезных возможностей.

Смайлики

Можно создать свой набор смайликов чата. Работать это будет так:

  • пользователь открывает специальное окошко и кликает по смайлику;
  • в поле ввода добавляется код смайлика (например, :sad: или :crazy:);
  • при выводе сообщения код смайлика заменяется на изображение.

Для начала нужно добавить контейнер со смайликами и кнопку для его открытия:

<form method='post' id='chat-form'>

<div class='emojis-container emojis-container_hidden' id='emojis'></div>

<img src='/images/emojis/happy.png' class='emoji-img' id='emoji-button'>

<input type='text' id='message-text' class='chat-form__input' placeholder='Введите сообщение'>

<input type='submit' class='chat-form__submit' value='=>'>

</form>

Задать стили:

.emojis-container {

position:absolute;

z-index:100;

background:#555;

border:1px solid #333;

padding:2px;

max-width:38%;

top:20px;

}

.emojis-container_hidden {

left:-9999999999999999px;

}

.emoji-img {

vertical-align:middle;

width:20px;

margin:1px;

cursor:pointer;

}

После этого нужно добавить скрипт для загрузки смайликов и открытия меню с ними:

//Создаем переменные

var emojis__container = document.getElementById('emojis');

var emojis__button = document.getElementById('emoji-button');

var showed = false;

function getEmojis() {//Получаем смайлики из PHP-файла

$.post('/includes/chat.php',{act: 'load-emojis'}).done(function (data) {

emojis__container.innerHTML = data;

});

}

function showEmojis() {//Добавляем отображение и скрытие окна

if(showed) {

emojis__container.setAttribute('class','emojis-container emojis-container_hidden');

showed = false;

} else {

emojis__container.setAttribute('class','emojis-container');

showed = true;

}

}

А теперь и функцию добавления смайлика в поле:

function addEmoji(title) {

messageInput.value += " " + title + " ";

//Тут же можно добавить закрытие контейнера

messageInput.focus();

}

После этого можно указать, когда вызываются функции:

$(document).ready(function (){

$(".emoji-add").on("click", function () {addEmoji($(this).attr('title'));});//Добавление

});

emojis__button.addEventListener('click',showEmojis);

getEmojis(); //Сразу же вызываем получение смайликов

Дальше можно приступить к загрузке смайликов и их преобразованию на PHP:

function getEmojis() {

$dir = '../images/emojis';

$echo = "";

$files = scandir($dir);

for($i = 0; $i != count($files); $i++) {

$ext = explode('.',$files[$i]);

if($ext[1] == 'png') {

$echo .= "<img src='/images/emojis/".$files[$i]."' title=':".$ext[0].":' class='emoji-img emoji-add'> ";

}

}

return $echo;

}

Эта функция сканирует папку со смайликами, а потом проверяет расширение файлов. Она очень удобна, потому что отображает в формате PNG все смайлики, которые были добавлены в папку.

Чтобы вызвать ее, добавьте еще один case в функцию switch() в конце обработчика:

case 'load-emojis':

$echo = getEmojis();

break;

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

function transformEmoji($message) {

$pattern = '/(\:\S*\:)/i'; //Паттерн смайлика

if(preg_match($pattern,$message,$matches)) {//Ищем все совпадения для смайлика одного типа — только :happy: или только :sad:

$path = explode(":",$matches[0]);

if(file_exists("../images/emojis/".$path[1].".png")) {//Проверяем, существует ли такое изображение

$message = preg_replace("/".$matches[0]."/","<img src='/images/emojis/$path[1].png' class='emoji-img'>",$message);//Заменяем код на изображение

}

$message = transformEmoji($message); //Повторяем, пока в $message есть коды смайликов

}

return $message;

}

Вызывается эта функция при загрузке сообщений:

$array['message'] = transformEmoji($array['message']);

Вот как это выглядит:

Ответ

Функцию addEmoji() можно немного изменить, чтобы добавить возможность отвечать какому-то конкретному пользователю. При нажатии на ник собеседника будет меняться текст в поле ввода.

Для этого в load() нужно изменить формат сообщений, добавив span к нику:

$echo .= "<div class='chat__message chat__message_$user[nick_color]'><b><span class='answer-span'>$user[login]</span>:</b> $array[message]</div>";

Дальше пишем саму функцию:

function addAnswer(login) {

messageInput.value = login + ", " + messageInput.value;

messageInput.focus();

А дальше добавляем вызов:

$(document).ready(function (){

$(".add-answer").on("click", function () {addEmoji($(this).text());});

});

Заключение

Как и всегда, одной статьи недостаточно, чтобы охватить все возможности PHP и JS для разработки чатов. Также можно добавить:

  • ответ на конкретные сообщения;
  • форматирование текста;
  • отправку аудио;
  • разные «комнаты»;
  • чат-бота и многое другое.

Научиться делать подобные вещи можно на курсе «PHP-разработчик». Там вы получите практические знания, которые позволят делать более сложные проекты.

Курс «PHP-разработчик»
Обширная программа для изучения PHP, состоящая из 4 курсов. Поможет с нуля овладеть популярным языком программирования, устроиться в IT-компанию вашей мечты или стать независимым разработчиком.
  • Живая обратная связь с преподавателями
  • Неограниченный доступ к материалам курса
  • Стажировка в компаниях-партнёрах
  • Дипломный проект от реального заказчика
  • Гарантия трудоустройства в компании-партнёры для выпускников, защитивших дипломные работы
Хочешь получать крутые статьи по программированию?
Подпишись на рассылку Skillbox