Если вы думаете, что PHP — это только генерация HTML и работа с базами данных, то вы сильно недооцениваете этот язык. Вебсокеты — это технология, которая позволяет серверу и клиенту обмениваться данными в реальном времени без постоянных запросов. И PHP здесь не пасёт задних. В этой статье разберём, как подружить PHP с вебсокетами, какие инструменты использовать и напишем простой чат.
Что такое вебсокеты и зачем они нужны
Обычный HTTP работает по принципу «запрос-ответ». Клиент спросил — сервер ответил. Если нужно получать обновления, приходится постоянно дёргать сервер. Это как звонить в дверь каждые пять секунд и спрашивать: «Ну что, письмо пришло?». Вебсокеты — это открытый канал связи. Один раз установили соединение — и общаетесь в обе стороны, пока не надоест. Сервер сам может отправить сообщение клиенту, как только что-то произошло. Это идеально для чатов, онлайн-игр, биржевых котировок и совместного редактирования документов.
В PHP долгое время не было встроенной поддержки вебсокетов. Приходилось использовать сторонние библиотеки или писать свой сервер на сокетах. Сейчас ситуация изменилась: есть Ratchet, Swoole, Workerman и другие. Мы рассмотрим Ratchet — простой и популярный вариант.
Как PHP работает с вебсокетами: основные подходы
Есть два основных способа подружить PHP с вебсокетами. Первый — использовать асинхронный сервер на самом PHP. Второй — держать отдельный сервис на Node.js или Go, а PHP только отправлять туда данные. Но мы же хотим всё на PHP? Тогда выбираем Ratchet.
Ratchet — это библиотека, которая реализует протокол WebSocket поверх ReactPHP. Она берёт на себя всю рутину: рукопожатие, фрейминг, управление соединениями. Вам остаётся только написать логику приложения.
Установка через Composer:
composer require cboden/ratchet
Теперь создадим простой сервер, который будет принимать сообщения и рассылать их всем подключённым клиентам. Это основа чата.
<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "Новое соединение! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
foreach ($this->clients as $client) {
if ($from !== $client) {
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
echo "Соединение закрыто ({$conn->resourceId})\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
$conn->close();
}
}
Этот класс реализует четыре метода: onOpen (кто-то подключился), onMessage (пришло сообщение), onClose (отключился), onError (ошибка). В onMessage мы просто пересылаем сообщение всем, кроме отправителя.
Теперь запустим сервер. Создадим файл server.php:
<?php
require __DIR__ . '/vendor/autoload.php';
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
$chat = new Chat();
$webSocket = new WsServer($chat);
$httpServer = new HttpServer($webSocket);
$server = IoServer::factory(
$httpServer,
8080
);
$server->run();
Запускаем в консоли:
php server.php
Сервер слушает порт 8080. Теперь нужно написать клиентскую часть на JavaScript, которая подключится к этому серверу.
const conn = new WebSocket('ws://localhost:8080');
conn.onopen = function(e) {
console.log('Соединение установлено');
};
conn.onmessage = function(e) {
console.log('Получено: ' + e.data);
};
conn.onclose = function(e) {
console.log('Соединение закрыто');
};
// Отправка сообщения
function sendMessage(message) {
conn.send(message);
}
Это минимальный рабочий пример. Но в реальном проекте нужно больше: аутентификация, приватные комнаты, хранение истории. Всё это можно реализовать на PHP.
Библиотеки и инструменты для работы с вебсокетами на PHP
Кроме Ratchet, есть и другие решения. Сравним их в таблице.
| Библиотека | Особенности | Сложность |
|---|---|---|
| Ratchet | Простой старт, хорошая документация, основан на ReactPHP | Низкая |
| Swoole | Высокая производительность, асинхронность, но требует установки расширения | Средняя |
| Workerman | Быстрый, поддерживает много протоколов, есть готовые решения | Средняя |
| ReactPHP | Низкоуровневый, гибкий, но нужно много писать самому | Высокая |
Для большинства задач Ratchet — оптимальный выбор. Он не требует установки дополнительных расширений (кроме самого PHP), легко ставится через Composer и имеет понятный API.
Практические советы и лайфхаки
Вот несколько вещей, которые сэкономят вам время и нервы.
Дальше — несколько советов по коду.
- Используйте хранилище для соединений. SplObjectStorage — это хорошо, но если сервер перезапустится, все клиенты потеряются. Для продакшена лучше хранить состояние в Redis или Memcached.
- Обрабатывайте разрывы. Клиент может отключиться в любой момент. Всегда очищайте ресурсы в onClose.
- Не блокируйте сервер. Если в onMessage вы делаете тяжёлый запрос к базе, остальные клиенты ждут. Используйте асинхронные вызовы или выносите логику в отдельные процессы.
- Логируйте всё. Вебсокеты — это постоянный поток данных. Без логов вы не поймёте, что пошло не так.
- Тестируйте под нагрузкой. Один клиент — это не показатель. Используйте инструменты вроде Artillery или написанные на PHP скрипты для эмуляции сотен соединений.
Ещё один момент: безопасность. WebSocket не имеет встроенной защиты от CSRF или XSS. Вы должны сами проверять входящие данные и аутентифицировать пользователей. Например, можно передавать токен при подключении и проверять его на сервере.
public function onOpen(ConnectionInterface $conn) {
$query = $conn->httpRequest->getUri()->getQuery();
parse_str($query, $params);
if (!isset($params['token']) || !validateToken($params['token'])) {
$conn->close();
return;
}
$this->clients->attach($conn);
}
Так вы отсеете левые подключения.
Заключение
PHP и вебсокеты — это не фантастика, а рабочая связка. С помощью Ratchet можно за пару часов поднять простой чат или систему уведомлений. Главное — понимать, что вебсокет-сервер работает постоянно, а не как обычный скрипт. Поэтому нужно следить за памятью, соединениями и ошибками. Если вы готовы к этому, PHP отлично справится с реальным временем.
Попробуйте написать свой чат на основе примеров выше. Это лучший способ разобраться. А если нужна помощь — обращайтесь в Digital-студию WNDER, мы поможем с любым проектом.
Студия WNDER



