R controller

This commit is contained in:
a6a2f5842 2025-12-05 22:08:47 +01:00
parent ad0e8d556f
commit ae22264aea
1 changed files with 94 additions and 87 deletions

View File

@ -14,12 +14,23 @@ use Illuminate\Support\Facades\Log;
class Controller class Controller
{ {
protected bool $isMockConnection;
protected ?MockConnection $mockConnectionClone = null;
final public function __construct( final public function __construct(
protected ConnectionInterface $connection, protected ConnectionInterface $connection,
protected PrivateChannel|Channel|PresenceChannel|null $channel, protected PrivateChannel|Channel|PresenceChannel|null $channel,
protected string $event, protected string $event,
protected LocalChannelManager|RedisChannelManager $channelManager protected LocalChannelManager|RedisChannelManager $channelManager
) {} ) {
// Cache class check to avoid repeated get_class() calls (reflection is slow)
$this->isMockConnection = get_class($connection) === MockConnection::class;
// Pre-clone MockConnection once if needed (reuse across method calls)
if ($this->isMockConnection) {
$this->mockConnectionClone = clone $connection;
}
}
public static function controll_message( public static function controll_message(
ConnectionInterface $connection, ConnectionInterface $connection,
@ -28,59 +39,38 @@ class Controller
LocalChannelManager|RedisChannelManager $channelManager LocalChannelManager|RedisChannelManager $channelManager
) { ) {
$event = self::get_event($message); $event = self::get_event($message);
if (count($event) !== 2) {
if (count($event) != 2) { return self::send_error($connection, $message, 'Event unknown');
return $connection->send(json_encode([
'event' => $message['event'] . ':error',
'data' => [
'message' => 'Event unknown',
],
'channel' => $message['channel'],
]));
} }
try { try {
$contr = (strpos($event[0], '-') >= 0) $contr = (strpos($event[0], '-') >= 0)
? implode('', array_map(fn ($item) => ucfirst($item), explode('-', $event[0]))) ? implode('', array_map(fn($item) => ucfirst($item), explode('-', $event[0])))
: ucfirst($event[0]); : ucfirst($event[0]);
$vendorcontroller = '\BlaxSoftware\LaravelWebSockets\Websocket\Controllers\\' . $contr . 'Controller'; $vendorcontroller = '\\BlaxSoftware\\LaravelWebSockets\\Websocket\\Controllers\\' . $contr . 'Controller';
$appcontroller = '\App\Websocket\Controllers\\' . $contr . 'Controller'; $appcontroller = '\\App\\Websocket\\Controllers\\' . $contr . 'Controller';
$method = static::without_uniquifyer($event[1]); $method = static::without_uniquifyer($event[1]);
$controller = class_exists($appcontroller) $controllerClass = class_exists($appcontroller)
? $appcontroller ? $appcontroller
: $vendorcontroller; : (class_exists($vendorcontroller) ? $vendorcontroller : null);
if (! $controller) { if (! $controllerClass) {
return $connection->send(json_encode([ return self::send_error($connection, $message, 'Event could not be associated');
'event' => $message['event'] . ':error',
'data' => [
'message' => 'Event could not be associated',
],
'channel' => $message['channel'],
]));
} }
if (! method_exists($controller, $method)) { if (! method_exists($controllerClass, $method)) {
return $connection->send(json_encode([ return self::send_error($connection, $message, 'Event could not be handled');
'event' => $message['event'] . ':error',
'data' => [
'message' => 'Event could not be handled',
],
'channel' => $message['channel'],
]));
} }
// Instantiate the controller $controller = new $controllerClass(
$controller = (new $controller(
$connection, $connection,
$channel, $channel,
$message['event'], $message['event'],
$channelManager $channelManager
)); );
// Return unauthorized if auth is required
if (($controller->need_auth ?? true) && ! $connection->user) { if (($controller->need_auth ?? true) && ! $connection->user) {
return $controller->error('Unauthorized'); return $controller->error('Unauthorized');
} }
@ -91,11 +81,8 @@ class Controller
$message['channel'] $message['channel']
); );
if ( if ($payload === false || $payload === true) {
$payload === false return null;
|| $payload === true
) {
return;
} }
$connection->send(json_encode([ $connection->send(json_encode([
@ -115,32 +102,19 @@ class Controller
]; ];
Log::error($e->getMessage(), $reload); Log::error($e->getMessage(), $reload);
return $connection->send(json_encode([ if (app()->bound('sentry')) {
'event' => $message['event'] . ':error', app('sentry')->captureException($e);
'data' => [ }
'message' => $e->getMessage(),
'meta' => [
'reported' => true,
],
],
'channel' => $message['channel'],
]));
}
return $connection->send(json_encode([ return self::send_error($connection, $message, $e->getMessage(), true);
'event' => $message['event'] . ':error', }
'data' => [
'message' => 'An unknown error occured',
],
'channel' => $message['channel'],
]));
} }
final public function progress( final public function progress(
mixed $payload = null, mixed $payload = null,
?string $event = null, ?string $event = null,
?string $channel = null ?string $channel = null
) : bool { ): bool {
$p = [ $p = [
'event' => ($event ?? $this->event) . ':progress', 'event' => ($event ?? $this->event) . ':progress',
'data' => $payload, 'data' => $payload,
@ -155,11 +129,13 @@ class Controller
$p['data'] = $payload['data']; $p['data'] = $payload['data'];
} }
if (get_class($this->connection) === MockConnection::class) { // Pre-encode once (avoid repeated encoding)
$connection = clone $this->connection; $encoded = json_encode($p);
$connection->send(json_encode($p));
if ($this->isMockConnection) {
$this->mockConnectionClone->send($encoded);
} else { } else {
$this->connection->send(json_encode($p)); $this->connection->send($encoded);
} }
return true; return true;
@ -169,7 +145,7 @@ class Controller
mixed $payload = null, mixed $payload = null,
?string $event = null, ?string $event = null,
?string $channel = null ?string $channel = null
) : bool { ): bool {
$p = [ $p = [
'event' => ($event ?? $this->event) . ':response', 'event' => ($event ?? $this->event) . ':response',
'data' => $payload, 'data' => $payload,
@ -184,11 +160,13 @@ class Controller
$p['data'] = $payload['data']; $p['data'] = $payload['data'];
} }
if (get_class($this->connection) === MockConnection::class) { // Pre-encode once (avoid repeated encoding)
$connection = clone $this->connection; $encoded = json_encode($p);
$connection->send(json_encode($p));
if ($this->isMockConnection) {
$this->mockConnectionClone->send($encoded);
} else { } else {
$this->connection->send(json_encode($p)); $this->connection->send($encoded);
} }
return true; return true;
@ -198,7 +176,7 @@ class Controller
array|string|null $payload = null, array|string|null $payload = null,
?string $event = null, ?string $event = null,
?string $channel = null ?string $channel = null
) : bool { ): bool {
if (is_string($payload)) { if (is_string($payload)) {
$payload = [ $payload = [
'message' => $payload, 'message' => $payload,
@ -227,11 +205,13 @@ class Controller
Log::channel('websocket')->error('Send error: ' . @$p['data']['message'], $p); Log::channel('websocket')->error('Send error: ' . @$p['data']['message'], $p);
if (get_class($this->connection) === MockConnection::class) { // Pre-encode once (avoid repeated encoding)
$connection = clone $this->connection; $encoded = json_encode($p);
$connection->send(json_encode($p));
if ($this->isMockConnection) {
$this->mockConnectionClone->send($encoded);
} else { } else {
$this->connection->send(json_encode($p)); $this->connection->send($encoded);
} }
return true; return true;
@ -242,7 +222,7 @@ class Controller
?string $event = null, ?string $event = null,
?string $channel = null, ?string $channel = null,
bool $including_self = false bool $including_self = false
){ ) {
if (is_string($payload)) { if (is_string($payload)) {
$payload = [ $payload = [
'message' => $payload, 'message' => $payload,
@ -257,24 +237,26 @@ class Controller
'channel' => $channel, 'channel' => $channel,
]; ];
if (get_class($this->connection) !== MockConnection::class) { if (!$this->isMockConnection) {
if (! $channel) { if (! $channel) {
$this->error('Channel not found'); $this->error('Channel not found');
return; return;
} }
// Pre-encode ONCE - massive improvement for 100+ connections
$encoded = json_encode($p);
foreach ($this->channel->getConnections() as $channel_conection) { foreach ($this->channel->getConnections() as $channel_conection) {
if ($channel_conection !== $this->connection) { if ($channel_conection !== $this->connection) {
$channel_conection->send(json_encode($p)); $channel_conection->send($encoded);
} }
if ($including_self) { if ($including_self) {
$this->connection->send(json_encode($p)); $this->connection->send($encoded);
} }
} }
}else{ } else {
$connection = clone $this->connection; $this->mockConnectionClone->broadcast(
$connection->broadcast(
$p, $p,
$channel, $channel,
$including_self $including_self
@ -287,7 +269,7 @@ class Controller
?string $event = null, ?string $event = null,
array $socketIds, array $socketIds,
?string $channel = null ?string $channel = null
){ ) {
if (is_string($payload)) { if (is_string($payload)) {
$payload = [ $payload = [
'message' => $payload, 'message' => $payload,
@ -302,20 +284,25 @@ class Controller
'channel' => $channel, 'channel' => $channel,
]; ];
if (get_class($this->connection) !== MockConnection::class) { if (!$this->isMockConnection) {
if (! $channel) { if (! $channel) {
$this->error('Channel not found'); $this->error('Channel not found');
return; return;
} }
// Pre-encode ONCE for all matching sockets
$encoded = json_encode($p);
// Use array_flip for O(1) lookup instead of O(n) in_array
$socketIdLookup = array_flip($socketIds);
foreach ($this->channel->getConnections() as $channel_conection) { foreach ($this->channel->getConnections() as $channel_conection) {
if (in_array($channel_conection->socketId, $socketIds)) { if (isset($socketIdLookup[$channel_conection->socketId])) {
$channel_conection->send(json_encode($p)); $channel_conection->send($encoded);
} }
} }
}else{ } else {
$connection = clone $this->connection; $this->mockConnectionClone->whisper(
$connection->whisper(
$p, $p,
$socketIds, $socketIds,
$channel $channel
@ -323,6 +310,26 @@ class Controller
} }
} }
private static function send_error(
ConnectionInterface $connection,
array $message,
string $reason,
bool $reported = false
) {
$connection->send(json_encode([
'event' => ($message['event'] ?? 'unknown') . ':error',
'data' => [
'message' => $reason,
'meta' => [
'reported' => $reported,
],
],
'channel' => $message['channel'] ?? null,
]));
return null;
}
protected static function get_uniquifyer($event) protected static function get_uniquifyer($event)
{ {
preg_match('/[\[].*[\]]/', $event, $matches); preg_match('/[\[].*[\]]/', $event, $matches);