From 636d9eccb8272936017c0249615fc6b75ce1a1ea Mon Sep 17 00:00:00 2001 From: "Fabian @ Blax Software" Date: Thu, 5 Mar 2026 12:26:56 +0100 Subject: [PATCH] I parent connection states --- src/Websocket/Handler.php | 41 +++++++++++++++++++++- src/Websocket/MockConnectionSocketPair.php | 38 ++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/Websocket/Handler.php b/src/Websocket/Handler.php index 4121195..d617021 100644 --- a/src/Websocket/Handler.php +++ b/src/Websocket/Handler.php @@ -573,8 +573,47 @@ class Handler implements MessageComponentInterface return; } - // Prefix-based routing: B: = broadcast, W: = whisper, else regular response + // Prefix-based routing: C: = connection data, B: = broadcast, W: = whisper, else regular response // Avoids JSON decode overhead for regular responses (most common path) + if (str_starts_with($data, 'C:')) { + // Connection data operation from child process. + // Lets controllers set/clear/reset arbitrary properties on the + // parent's in-memory connection object via IPC. + $op = substr($data, 2); + + if ($op === 'RESET') { + // Clear auth state + unset($connection->authLoaded); + $connection->user = null; + + // Clear any custom connection data that was stored via C:SET + foreach (($connection->_connectionDataKeys ?? []) as $key => $_) { + unset($connection->$key); + } + $connection->_connectionDataKeys = []; + } elseif (str_starts_with($op, 'SET:')) { + // C:SET:key:json_value + $rest = substr($op, 4); + $pos = strpos($rest, ':'); + if ($pos !== false) { + $key = substr($rest, 0, $pos); + $value = json_decode(substr($rest, $pos + 1)); + $connection->$key = $value; + $connection->_connectionDataKeys ??= []; + $connection->_connectionDataKeys[$key] = true; + } + } elseif (str_starts_with($op, 'DEL:')) { + // C:DEL:key + $key = substr($op, 4); + unset($connection->$key); + if (isset($connection->_connectionDataKeys[$key])) { + unset($connection->_connectionDataKeys[$key]); + } + } + + return; + } + if (str_starts_with($data, 'B:')) { $bm = json_decode(substr($data, 2), true); $this->broadcast( diff --git a/src/Websocket/MockConnectionSocketPair.php b/src/Websocket/MockConnectionSocketPair.php index 8e59c4d..959c053 100644 --- a/src/Websocket/MockConnectionSocketPair.php +++ b/src/Websocket/MockConnectionSocketPair.php @@ -75,6 +75,44 @@ class MockConnectionSocketPair implements ConnectionInterface // No-op for mock } + /** + * Reset all user-set connection state on the parent process. + * + * Clears auth state (user, authLoaded) and any custom connection data + * that was stored via setConnectionData(). Channels remain subscribed. + * + * Used after logout so the next WS message re-authenticates from Redis. + */ + public function resetConnection(): void + { + $this->ipc->sendToParent('C:RESET'); + } + + /** + * Store a custom key-value pair on the parent's connection object. + * + * The value is JSON-serialized over IPC and set as $connection->$key + * on the parent process. Readable by any subsequent controller via + * $this->connection->$key (proxied through __get). + */ + public function setConnectionData(string $key, mixed $value): void + { + // Update local child copy for immediate reads within this request + $this->realConnection->$key = $value; + + // Signal parent to persist the change + $this->ipc->sendToParent('C:SET:' . $key . ':' . json_encode($value)); + } + + /** + * Remove a custom key from the parent's connection object. + */ + public function clearConnectionData(string $key): void + { + unset($this->realConnection->$key); + $this->ipc->sendToParent('C:DEL:' . $key); + } + /** * Magic getter to proxy properties from real connection. */