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

Digital-студия WNDER

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

Обычный сайт работает по схеме «запрос-ответ». Вы открываете страницу, браузер просит данные у сервера, сервер отвечает — и всё, связь закончилась. Чтобы узнать что-то новое, нужно снова отправить запрос, например, перезагрузить страницу или нажать кнопку. Это как звонить другу каждый раз, когда хочешь узнать, пришло ли сообщение.

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

Для Yii2 это означает, что вы можете обновлять уведомления, сообщения в чате или любые данные на странице без её перезагрузки. Пользователю кажется, что сайт «живой», и это сильно улучшает впечатление.

Как это работает в Yii2

Yii2 — это PHP-фреймворк. Он работает на сервере, но для вебсокетов нужен постоянный процесс, который слушает соединения. Обычный PHP-скрипт живёт недолго: обработал запрос и умер. Поэтому для вебсокетов используют отдельный сервер, например, Ratchet или Workerman. Они написаны на PHP, но запускаются как демон — постоянно работающий процесс.

Yii2 выступает в роли моста: он управляет бизнес-логикой, хранит данные в базе, а вебсокет-сервер обрабатывает соединения и пересылает сообщения. Они общаются между собой через базу данных, Redis или напрямую через вызовы.

Схема такая: браузер открывает вебсокет-соединение с сервером, например, на порту 8080. Когда происходит событие (новое сообщение, изменение цены), сервер отправляет данные через это соединение. Браузер ловит их и обновляет страницу без перезагрузки.

Установка и настройка Workerman

Workerman — один из самых популярных инструментов для вебсокетов в PHP. Он лёгкий, быстрый и легко интегрируется с Yii2.

Для начала установим его через Composer. В корне вашего проекта выполните команду:

composer require workerman/workerman

После этого создадим консольный контроллер в Yii2, который будет запускать вебсокет-сервер. Например, создадим файл commands/WebSocketController.php:

<?php
namespace app\commands;

use yii\console\Controller;
use Workerman\Worker;
use Workerman\Connection\TcpConnection;

class WebSocketController extends Controller
{
    public function actionStart()
    {
        $worker = new Worker('websocket://0.0.0.0:8080');
        
        $worker->onConnect = function (TcpConnection $connection) {
            echo "Новое соединение\n";
        };
        
        $worker->onMessage = function (TcpConnection $connection, $data) {
            $connection->send('Вы сказали: ' . $data);
        };
        
        $worker->onClose = function (TcpConnection $connection) {
            echo "Соединение закрыто\n";
        };
        
        Worker::runAll();
    }
}

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

php yii websocket/start

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

Подключение клиента на JavaScript

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

<!DOCTYPE html>
<html>
<head>
    <title>Чат на вебсокетах</title>
</head>
<body>
    <input type='text' id='message' placeholder='Введите сообщение'>
    <button onclick='sendMessage()'>Отправить</button>
    <div id='output'></div>

    <script>
        var socket = new WebSocket('ws://localhost:8080');
        
        socket.onopen = function() {
            console.log('Соединение установлено');
        };
        
        socket.onmessage = function(event) {
            var output = document.getElementById('output');
            output.innerHTML += '<p>' + event.data + '</p>';
        };
        
        function sendMessage() {
            var message = document.getElementById('message').value;
            socket.send(message);
            document.getElementById('message').value = '';
        }
    </script>
</body>
</html>

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

Отправка данных из Yii2 в вебсокет

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

Для этого можно использовать Redis или просто вызывать метод вебсокет-сервера. Проще всего — использовать Redis Pub/Sub. Вебсокет-сервер подписывается на канал, а Yii2 публикует в него сообщения.

Вот как можно настроить Workerman для работы с Redis:

<?php
namespace app\commands;

use yii\console\Controller;
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Redis\Client as RedisClient;

class WebSocketController extends Controller
{
    public function actionStart()
    {
        $worker = new Worker('websocket://0.0.0.0:8080');
        
        $worker->onWorkerStart = function () {
            $redis = new RedisClient('redis://localhost:6379');
            $redis->subscribe(['chat'], function ($channel, $message) {
                // Отправить сообщение всем клиентам
                foreach (Worker::getConnections() as $connection) {
                    $connection->send($message);
                }
            });
        };
        
        $worker->onMessage = function (TcpConnection $connection, $data) {
            $connection->send('Вы сказали: ' . $data);
        };
        
        Worker::runAll();
    }
}

А в Yii2, например, в модели сообщения, при сохранении отправляем в Redis:

<?php
use yii\redis\Connection;

$redis = Yii::$app->redis;
$redis->publish('chat', json_encode(['message' => 'Привет всем!']));

Теперь, когда происходит событие в Yii2, все клиенты, подключённые к вебсокету, получат уведомление.

Практические советы и подводные камни

Вот несколько вещей, которые стоит знать, если вы решите использовать вебсокеты в Yii2:

  • Не используйте вебсокеты для всего подряд. Для простых обновлений лучше подойдут AJAX-запросы. Вебсокеты оправданы, когда данные должны приходить мгновенно и часто.
  • Учитывайте ограничения сервера. Каждое соединение требует ресурсов. Если у вас тысячи одновременных подключений, нужен мощный сервер или горизонтальное масштабирование.
  • Настройте переподключение. Браузеры нестабильны, соединение может оборваться. На клиенте добавьте логику переподключения через несколько секунд.
Лайфхак: Используйте библиотеку workerman/channel для общения между процессами. Это упрощает отправку сообщений на разные серверы.

Таблица сравнения: вебсокеты и обычные AJAX

Параметр Вебсокеты AJAX
Скорость обновления Мгновенно, сервер сам инициирует Зависит от частоты запросов
Нагрузка на сервер Одно соединение на клиента Множество запросов
Сложность реализации Средняя, требует настройки Простая
Поддержка браузеров Все современные Все

Что в итоге

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

Начните с простого примера, который я показал, а затем усложняйте по мере необходимости. Через пару часов практики вы освоитесь и сможете добавить «живость» своему проекту.

Студия WNDER