laravel-websockets/src/Websocket/Controller.php

263 lines
7.9 KiB
PHP
Raw Normal View History

2025-01-16 07:54:02 +00:00
<?php
declare(strict_types=1);
namespace BlaxSoftware\LaravelWebSockets\Websocket;
use BlaxSoftware\LaravelWebSockets\ChannelManagers\LocalChannelManager;
use BlaxSoftware\LaravelWebSockets\ChannelManagers\RedisChannelManager;
use BlaxSoftware\LaravelWebSockets\Channels\Channel;
use BlaxSoftware\LaravelWebSockets\Channels\PresenceChannel;
use BlaxSoftware\LaravelWebSockets\Channels\PrivateChannel;
use Ratchet\ConnectionInterface;
2025-01-17 09:45:53 +00:00
use Illuminate\Support\Facades\Log;
2025-01-16 07:54:02 +00:00
class Controller
{
final public function __construct(
protected ConnectionInterface $connection,
protected PrivateChannel|Channel|PresenceChannel|null $channel,
protected string $event,
protected LocalChannelManager|RedisChannelManager $channelManager
) {}
public static function controll_message(
ConnectionInterface $connection,
PrivateChannel $channel,
array $message,
LocalChannelManager|RedisChannelManager $channelManager
) {
$event = self::get_event($message);
if (count($event) != 2) {
return $connection->send(json_encode([
'event' => $message['event'] . ':error',
'data' => [
'message' => 'Event unknown',
],
'channel' => $message['channel'],
]));
}
try {
$controller = (strpos($event[0], '-') >= 0)
? implode('', array_map(fn ($item) => ucfirst($item), explode('-', $event[0])))
: ucfirst($event[0]);
$controller = '\BlaxSoftware\LaravelWebSockets\Websocket\Controllers\\' . $controller . 'Controller';
$method = static::without_uniquifyer($event[1]);
if (! class_exists($controller)) {
return $connection->send(json_encode([
'event' => $message['event'] . ':error',
'data' => [
'message' => 'Event could not be associated',
],
'channel' => $message['channel'],
]));
}
if (! method_exists($controller, $method)) {
return $connection->send(json_encode([
'event' => $message['event'] . ':error',
'data' => [
'message' => 'Event could not be handled',
],
'channel' => $message['channel'],
]));
}
// Instantiate the controller
$controller = (new $controller(
$connection,
$channel,
$message['event'],
$channelManager
));
// Return unauthorized if auth is required
if (($controller->need_auth ?? true) && ! $connection->user) {
return $controller->error('Unauthorized');
}
$payload = $controller->$method(
$connection,
$message['data'],
$message['channel']
);
if (
$payload === false
|| $payload === true
) {
return;
}
$connection->send(json_encode([
'event' => $message['event'] . ':response',
'data' => $payload,
'channel' => $message['channel'],
]));
return $payload;
} catch (\Exception $e) {
$reload = [
'event' => @$message['event'],
'data' => @$message['data'],
'channel' => @$message['channel'],
'line' => $e->getFile() . ':' . $e->getLine(),
];
2025-01-17 09:45:53 +00:00
Log::error($e->getMessage(), $reload);
2025-01-16 07:54:02 +00:00
return $connection->send(json_encode([
'event' => $message['event'] . ':error',
'data' => [
'message' => 'Error: ' . $e->getMessage(),
'meta' => [
'reported' => true,
],
],
'channel' => $message['channel'],
]));
}
return $connection->send(json_encode([
'event' => $message['event'] . ':error',
'data' => [
'message' => 'An unknown error occured',
],
'channel' => $message['channel'],
]));
}
final public function broadcast(
mixed $payload,
?string $event = null,
?string $channel = null,
bool $including_self = false
) : void {
if (! $this->channel) {
$this->error('Channel not found');
return;
}
foreach ($this->channel->getConnections() as $channel_conection) {
if ($channel_conection !== $this->connection) {
$channel_conection->send(json_encode([
'event' => ($event ?? $this->event),
'data' => $payload,
'channel' => $channel ?? $this->channel->getName(),
]));
}
if ($including_self) {
$this->connection->send(json_encode([
'event' => ($event ?? $this->event),
'data' => $payload,
'channel' => $channel ?? $this->channel->getName(),
]));
}
}
}
final public function success(
mixed $payload = null,
?string $event = null,
?string $channel = null
) : bool {
$p = [
'event' => ($event ?? $this->event) . ':response',
'data' => $payload,
'channel' => $channel ?? $this->channel->getName(),
];
// if payload only contains key "data"
if (
count($p) === 1
&& isset($payload['data'])
) {
$p['data'] = $payload['data'];
}
if (get_class($this->connection) === MockConnection::class) {
$connection = clone $this->connection;
$connection->send(json_encode($p));
} else {
$this->connection->send(json_encode($p));
}
return true;
}
final public function error(
array|string|null $payload = null,
?string $event = null,
?string $channel = null
) : bool {
if (is_string($payload)) {
$payload = [
'message' => $payload,
];
}
$p = [
'event' => ($event ?? $this->event) . ':error',
'data' => $payload,
'channel' => $channel ?? $this->channel->getName(),
];
// if payload only contains key "data"
if (
count($p) === 1
&& isset($payload['data'])
) {
$p['data'] = $payload['data'];
}
// get line from where this is called from
$trace = debug_backtrace();
$p['data']['trace'] = $trace
? $trace[0]['line']
: null;
// log
2025-01-17 09:45:53 +00:00
Log::channel('websocket')->error('Send error: ' . $p['data']['message'], $p);
2025-01-16 07:54:02 +00:00
if (get_class($this->connection) === MockConnection::class) {
$connection = clone $this->connection;
$connection->send(json_encode($p));
} else {
$this->connection->send(json_encode($p));
}
return true;
}
protected static function get_uniquifyer($event)
{
preg_match('/[\[].*[\]]/', $event, $matches);
if (count($matches) === 1) {
$uniqiueifier = $matches[0];
}
return $uniqiueifier ?? null;
}
protected static function without_uniquifyer($event)
{
return preg_replace('/[\[].*[\]]/', '', $event);
}
private static function get_event($message)
{
$event = explode('.', $message['event']);
if (strpos($event[0], 'pusher:') > -1) {
$event = explode(':', $event[0]);
}
return $event;
}
}