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

Digital-студия WNDER

Ссылка на оригинальную статью

Что такое вебсокеты и зачем они нужны

Вебсокеты — это протокол, который обеспечивает двустороннюю связь между клиентом и сервером. В отличие от обычного HTTP, где клиент отправляет запрос и получает ответ, вебсокеты позволяют серверу отправлять данные клиенту самостоятельно, без запроса. Это идеально для чатов, уведомлений, онлайн-игр и других приложений реального времени.

Представьте, что вы сидите в кафе и общаетесь с другом. Обычный HTTP — это когда вы каждый раз встаете, подходите к стойке и спрашиваете: "Есть ли новые сообщения?" Вебсокеты — это когда официант сам приносит вам кофе, как только он готов. Экономия времени и ресурсов.

Подготовка окружения и установка необходимых компонентов

Для работы нам понадобится Yii2 (базовый или продвинутый шаблон), PHP версии 7.4 или выше, Composer и Node.js (для запуска WebSocket-сервера). Если у вас еще нет Yii2, установите его через Composer:

composer create-project --prefer-dist yiisoft/yii2-app-basic chat

Теперь установим расширение для работы с вебсокетами. Одно из популярных — ratchet/pawl для серверной части и textalk/websocket для клиента. Но мы пойдем более простым путем — используем библиотеку Workerman, которая отлично работает с PHP и не требует Node.js.

composer require workerman/workerman

Workerman — это асинхронный PHP-фреймворк для создания сетевых приложений. Он прост в использовании и поддерживает вебсокеты из коробки.

Создание WebSocket-сервера

Создадим файл server.php в корне проекта. Он будет запускать WebSocket-сервер, который будет принимать сообщения от клиентов и пересылать их всем подключенным пользователям.

<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;

require_once __DIR__ . '/vendor/autoload.php';

$ws_worker = new Worker("websocket://0.0.0.0:8080");
$ws_worker->count = 1;

// Массив для хранения всех подключенных клиентов
$clients = [];

$ws_worker->onConnect = function (TcpConnection $connection) use (&$clients) {
    $clients[$connection->id] = $connection;
    echo "Новый клиент: " . $connection->id . PHP_EOL;
};

$ws_worker->onMessage = function (TcpConnection $connection, $data) use (&$clients) {
    $data = json_decode($data, true);
    $message = [
        'user' => $data['user'],
        'text' => $data['text'],
        'time' => date('H:i:s')
    ];
    // Отправляем сообщение всем клиентам
    foreach ($clients as $client) {
        $client->send(json_encode($message));
    }
};

$ws_worker->onClose = function (TcpConnection $connection) use (&$clients) {
    unset($clients[$connection->id]);
    echo "Клиент отключился: " . $connection->id . PHP_EOL;
};

Worker::runAll();

Здесь мы создаем WebSocket-сервер на порту 8080. При подключении нового клиента добавляем его в массив $clients. При получении сообщения от одного клиента отправляем его всем остальным. Просто и эффективно.

Интеграция с Yii2: создание контроллера и представления

Теперь создадим контроллер в Yii2, который будет обрабатывать запросы чата. Создадим ChatController в папке controllers.

<?php
namespace app\controllers;

use Yii;
use yii\web\Controller;

class ChatController extends Controller
{
    public function actionIndex()
    {
        return $this->render('index');
    }
}

В представлении views/chat/index.php создадим интерфейс чата. Нам понадобится поле для ввода имени пользователя, поле для ввода сообщения и область для отображения сообщений. Подключим JavaScript для работы с вебсокетами.

<div class="chat-container">
    <div class="chat-header">Чат</div>
    <div class="chat-messages" id="messages"></div>
    <div class="chat-input">
        <input type="text" id="user" placeholder="Ваше имя" />
        <input type="text" id="message" placeholder="Введите сообщение..." />
        <button id="send">Отправить</button>
    </div>
</div>

Стилизуем все это в CSS, но для краткости опустим. Добавим JavaScript-код, который будет подключаться к WebSocket-серверу и обрабатывать отправку и получение сообщений.

const ws = new WebSocket('ws://localhost:8080');
const messages = document.getElementById('messages');
const userInput = document.getElementById('user');
const messageInput = document.getElementById('message');
const sendButton = document.getElementById('send');

ws.onopen = function() {
    console.log('Соединение установлено');
};

ws.onmessage = function(event) {
    const data = JSON.parse(event.data);
    const messageElement = document.createElement('div');
    messageElement.innerHTML = '<strong>' + data.user + '</strong>: ' + data.text + ' <em>(' + data.time + ')</em>';
    messages.appendChild(messageElement);
    messages.scrollTop = messages.scrollHeight;
};

sendButton.addEventListener('click', function() {
    const user = userInput.value.trim();
    const text = messageInput.value.trim();
    if (user && text) {
        ws.send(JSON.stringify({user: user, text: text}));
        messageInput.value = '';
    }
});

messageInput.addEventListener('keypress', function(e) {
    if (e.key === 'Enter') {
        sendButton.click();
    }
});

В этом коде мы создаем WebSocket-соединение с адресом ws://localhost:8080. При получении сообщения от сервера добавляем его в блок сообщений. При нажатии на кнопку или Enter отправляем данные на сервер.

Запуск и тестирование

Запустим WebSocket-сервер командой:

php server.php start

Затем запустим встроенный сервер Yii2:

php yii serve

Откройте в браузере http://localhost:8080 (или другой порт, который вы указали) и попробуйте отправить сообщение. Откройте второе окно браузера, чтобы увидеть, как сообщения передаются между клиентами.

Правило: Всегда проверяйте, что порт для WebSocket-сервера не занят другим приложением. Используйте команду netstat -tulpn | grep 8080 для проверки.

Практические советы и лайфхаки

Вот несколько советов, которые пригодятся при разработке чатов на Yii2 с вебсокетами:

  • Используйте Redis или базу данных для хранения истории сообщений, чтобы при перезагрузке страницы они не терялись.
  • Для продакшена настройте SSL-сертификат, чтобы WebSocket-соединение было защищенным (wss://).
  • Ограничьте количество сообщений в чате, чтобы не перегружать сервер.
  • Используйте аутентификацию пользователей через Yii2, чтобы знать, кто пишет сообщения.

Сравнение подходов к созданию чатов:

Подход Преимущества Недостатки
Polling (частые запросы) Простота реализации Высокая нагрузка на сервер, задержки
Long Polling Меньше запросов Сложность, проблемы с масштабированием
WebSockets Реальное время, низкая нагрузка Требуется специальный сервер

Заключение

Мы создали простой чат на Yii2 с использованием вебсокетов. Вы узнали, как работает WebSocket-сервер, как интегрировать его с Yii2 и как написать клиентскую часть. Это основа, которую можно расширять: добавлять базу данных, аутентификацию, комнаты, историю сообщений и многое другое.

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

Студия WNDER