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

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

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

Если знаний ещё недостаточно, обратите внимание на курс «Веб-разработчик c нуля до PRO».

Евгений Кучерявый

Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.


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

Удобство для пользователей превыше всего. Позаботьтесь, чтобы чат соответствовал современным требованиям:

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

Каркас чата на 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 для разработки чатов. С помощью этих языков легко добавить:

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

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

Курс

Веб-разработчик c нуля до PRO


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

Хочешь получать крутые статьи по программированию?
Подпишись на рассылку Skillbox