laravel-websockets/src/Services/WebsocketService.php

227 lines
6.8 KiB
PHP
Raw Normal View History

2025-05-08 08:54:11 +00:00
<?php
declare(strict_types=1);
namespace BlaxSoftware\LaravelWebSockets\Services;
2026-02-03 14:03:50 +00:00
use BlaxSoftware\LaravelWebSockets\Broadcast\BroadcastClient;
2025-05-08 08:54:11 +00:00
class WebsocketService
{
2026-02-03 14:03:50 +00:00
/**
* Send a message via WebSocket.
*
* Automatically uses the efficient Unix socket broadcast when available,
* falling back to creating a new WebSocket connection when not.
*/
2025-09-15 12:29:07 +00:00
public static function send(
string $event,
mixed $data,
$channel = 'websocket'
2026-02-03 14:03:50 +00:00
) {
// Try efficient broadcast socket first (Unix socket IPC)
if (ws_available()) {
$success = ws_broadcast($event, is_array($data) ? $data : ['data' => $data], $channel ?? 'websocket');
if ($success) {
return (object)['success' => true, 'method' => 'broadcast_socket'];
}
// Fall through to WebSocket client if broadcast socket fails
}
// Fallback: Create new WebSocket connection (slower, for when broadcast socket not available)
return static::sendViaWebSocket($event, $data, $channel);
}
/**
* Send a message to specific socket IDs only.
*
* @param string $event Event name
* @param mixed $data Event data
* @param array $sockets Target socket IDs
* @param string $channel Channel name
* @return bool Success
*/
public static function whisper(
string $event,
mixed $data,
array $sockets,
string $channel = 'websocket'
): bool {
if (!ws_available()) {
return false;
}
return ws_whisper($event, is_array($data) ? $data : ['data' => $data], $sockets, $channel);
}
/**
* Broadcast to all except specified socket IDs.
*
* @param string $event Event name
* @param mixed $data Event data
* @param array $excludeSockets Socket IDs to exclude
* @param string $channel Channel name
* @return bool Success
*/
public static function broadcastExcept(
string $event,
mixed $data,
array $excludeSockets,
string $channel = 'websocket'
): bool {
if (!ws_available()) {
return false;
}
return ws_broadcast_except($event, is_array($data) ? $data : ['data' => $data], $excludeSockets, $channel);
}
/**
* Send a message by creating a new WebSocket connection.
* This is the legacy method, kept for fallback when broadcast socket is unavailable.
*/
protected static function sendViaWebSocket(
string $event,
mixed $data,
$channel = 'websocket'
2025-09-15 12:29:07 +00:00
) {
$client = new \WebSocket\Client('ws://0.0.0.0:6001/app/'.config('websockets.apps.0.id'), [
'timeout' => 5,
'headers' => [],
]);
// Read connection_established
$client->receive();
// Subscribe (public channel)
$client->send(json_encode([
'event' => 'pusher:subscribe',
'data' => ['channel' => 'websocket'],
]));
// (Optionally read subscription_succeeded)
$client->receive();
// Send event to be processed by Handler
$client->send(json_encode([
'event' => $event,
'channel' => $channel ?? 'websocket',
'data' => $data,
]));
// Read any response your controller might send (optional)
$response = $client->receive();
$client->close();
return json_decode($response);
2025-05-08 08:54:11 +00:00
}
2025-09-14 13:00:27 +00:00
2025-09-15 08:23:07 +00:00
public static function resetAllTracking()
{
2025-09-15 12:34:50 +00:00
$previousCache = config('cache.default');
2025-09-15 08:23:07 +00:00
config(['cache.default' => 'file']);
cache()->forget('ws_active_channels');
cache()->forget('ws_socket_auth');
cache()->forget('ws_socket_auth_users');
2025-09-15 08:25:38 +00:00
cache()->forget('ws_socket_authed_users');
2025-09-15 08:23:07 +00:00
cache()->forget('ws_channel_connections');
cache()->forget('ws_connection');
2025-09-15 12:34:50 +00:00
config(['cache.default' => $previousCache]);
2025-09-15 08:23:07 +00:00
return true;
}
2025-09-15 08:25:38 +00:00
2025-09-15 08:23:07 +00:00
public static function getAuth(string $socketId)
2025-09-14 13:00:27 +00:00
{
2025-09-15 12:34:50 +00:00
$previousCache = config('cache.default');
2025-09-14 13:00:27 +00:00
config(['cache.default' => 'file']);
2025-09-15 12:34:50 +00:00
$r = cache()->get('ws_socket_auth_' . str()->slug($socketId));
config(['cache.default' => $previousCache]);
return $r;
2025-09-14 13:00:27 +00:00
}
public static function getChannelConnections(string $channelName)
{
2025-09-15 12:34:50 +00:00
$previousCache = config('cache.default');
2025-09-14 13:00:27 +00:00
config(['cache.default' => 'file']);
2025-09-15 12:34:50 +00:00
$r = cache()->get('ws_channel_connections_' . $channelName);
config(['cache.default' => $previousCache]);
return $r;
2025-09-14 13:00:27 +00:00
}
public static function getActiveChannels()
{
2025-09-15 12:34:50 +00:00
$previousCache = config('cache.default');
2025-09-14 13:00:27 +00:00
config(['cache.default' => 'file']);
2025-09-15 12:34:50 +00:00
$r = cache()->get('ws_active_channels');
config(['cache.default' => $previousCache]);
return $r;
2025-09-14 13:00:27 +00:00
}
public static function getConnection(string $socketId)
{
2025-09-15 12:34:50 +00:00
$previousCache = config('cache.default');
2025-09-14 13:00:27 +00:00
config(['cache.default' => 'file']);
2025-09-15 12:34:50 +00:00
$r = cache()->get('ws_connection_' . str()->slug($socketId));
config(['cache.default' => $previousCache]);
return $r;
2025-09-14 13:00:27 +00:00
}
2025-09-15 08:20:13 +00:00
public static function getAuthedUsers()
{
2025-09-15 12:34:50 +00:00
$previousCache = config('cache.default');
2025-09-15 08:20:13 +00:00
config(['cache.default' => 'file']);
2025-09-15 12:34:50 +00:00
$r = cache()->get('ws_socket_authed_users') ?? [];
config(['cache.default' => $previousCache]);
return $r;
2025-09-15 08:20:13 +00:00
}
public static function isUserConnected($userId)
{
2025-09-15 10:40:25 +00:00
return in_array($userId, array_values(static::getAuthedUsers()));
2025-09-15 08:20:13 +00:00
}
public static function getUserSocketIds($userId)
{
$socket_ids = [];
2025-09-15 10:40:25 +00:00
foreach (static::getAuthedUsers() as $socket_id => $u_id) {
2025-09-15 08:20:13 +00:00
if ($u_id == $userId) {
$socket_ids[] = $socket_id;
}
}
return $socket_ids;
}
2025-09-15 10:40:25 +00:00
public static function setUserAuthed($socketId, $user)
{
$authed_users = static::getAuthedUsers();
$authed_users[$socketId] = $user->id;
2025-09-15 12:34:50 +00:00
$previousCache = config('cache.default');
config(['cache.default' => 'file']);
2025-09-15 10:40:25 +00:00
cache()->forever('ws_socket_authed_users', $authed_users);
cache()->forever('ws_socket_auth_' . str()->slug($socketId), $user);
2025-09-15 12:34:50 +00:00
config(['cache.default' => $previousCache]);
2025-09-15 10:40:25 +00:00
return static::getAuthedUsers();
}
public static function clearUserAuthed($socketId)
{
$authed_users = static::getAuthedUsers();
unset($authed_users[$socketId]);
2025-09-15 12:34:50 +00:00
$previousCache = config('cache.default');
config(['cache.default' => 'file']);
2025-09-15 10:40:25 +00:00
cache()->forever('ws_socket_authed_users', $authed_users);
cache()->forget('ws_socket_auth_' . str()->slug($socketId));
2025-09-15 12:34:50 +00:00
config(['cache.default' => $previousCache]);
2025-09-15 10:40:25 +00:00
return static::getAuthedUsers();
}
2025-05-08 08:54:11 +00:00
}