diff --git a/config/websockets.php b/config/websockets.php index 40aaa24eeb..9d0804a39b 100644 --- a/config/websockets.php +++ b/config/websockets.php @@ -91,4 +91,13 @@ */ 'passphrase' => null, ], + + /* + * Channel Manager + * This class handles how channel persistence is handled. + * By default, persistence is stored in an array by the running webserver. + * The only requirement is that the class should implement + * `ChannelManager` interface provided by this package. + */ + 'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class, ]; diff --git a/src/WebSockets/Channels/ChannelManager.php b/src/WebSockets/Channels/ChannelManager.php index 53391920c0..cacff7efac 100644 --- a/src/WebSockets/Channels/ChannelManager.php +++ b/src/WebSockets/Channels/ChannelManager.php @@ -4,78 +4,15 @@ use Ratchet\ConnectionInterface; -class ChannelManager +interface ChannelManager { - /** @var string */ - protected $appId; + public function findOrCreate(string $appId, string $channelName): Channel; - /** @var array */ - protected $channels = []; + public function find(string $appId, string $channelName): ?Channel; - public function findOrCreate(string $appId, string $channelName): Channel - { - if (! isset($this->channels[$appId][$channelName])) { - $channelClass = $this->determineChannelClass($channelName); + public function getChannels(string $appId): array; - $this->channels[$appId][$channelName] = new $channelClass($channelName); - } + public function getConnectionCount(string $appId): int; - return $this->channels[$appId][$channelName]; - } - - public function find(string $appId, string $channelName): ?Channel - { - return $this->channels[$appId][$channelName] ?? null; - } - - protected function determineChannelClass(string $channelName): string - { - if (starts_with($channelName, 'private-')) { - return PrivateChannel::class; - } - - if (starts_with($channelName, 'presence-')) { - return PresenceChannel::class; - } - - return Channel::class; - } - - public function getChannels(string $appId): array - { - return $this->channels[$appId] ?? []; - } - - public function getConnectionCount(string $appId): int - { - return collect($this->getChannels($appId)) - ->sum(function ($channel) { - return count($channel->getSubscribedConnections()); - }); - } - - public function removeFromAllChannels(ConnectionInterface $connection) - { - if (! isset($connection->app)) { - return; - } - - /* - * Remove the connection from all channels. - */ - collect(array_get($this->channels, $connection->app->id, []))->each->unsubscribe($connection); - - /* - * Unset all channels that have no connections so we don't leak memory. - */ - collect(array_get($this->channels, $connection->app->id, [])) - ->reject->hasConnections() - ->each(function (Channel $channel, string $channelName) use ($connection) { - unset($this->channels[$connection->app->id][$channelName]); - }); - - if (count(array_get($this->channels, $connection->app->id, [])) === 0) { - unset($this->channels[$connection->app->id]); - } - } + public function removeFromAllChannels(ConnectionInterface $connection); } diff --git a/src/WebSockets/Channels/ChannelManagers/ArrayChannelManager.php b/src/WebSockets/Channels/ChannelManagers/ArrayChannelManager.php new file mode 100644 index 0000000000..d529adf9f0 --- /dev/null +++ b/src/WebSockets/Channels/ChannelManagers/ArrayChannelManager.php @@ -0,0 +1,85 @@ +channels[$appId][$channelName])) { + $channelClass = $this->determineChannelClass($channelName); + + $this->channels[$appId][$channelName] = new $channelClass($channelName); + } + + return $this->channels[$appId][$channelName]; + } + + public function find(string $appId, string $channelName): ?Channel + { + return $this->channels[$appId][$channelName] ?? null; + } + + protected function determineChannelClass(string $channelName): string + { + if (starts_with($channelName, 'private-')) { + return PrivateChannel::class; + } + + if (starts_with($channelName, 'presence-')) { + return PresenceChannel::class; + } + + return Channel::class; + } + + public function getChannels(string $appId): array + { + return $this->channels[$appId] ?? []; + } + + public function getConnectionCount(string $appId): int + { + return collect($this->getChannels($appId)) + ->sum(function ($channel) { + return count($channel->getSubscribedConnections()); + }); + } + + public function removeFromAllChannels(ConnectionInterface $connection) + { + if (! isset($connection->app)) { + return; + } + + /* + * Remove the connection from all channels. + */ + collect(array_get($this->channels, $connection->app->id, []))->each->unsubscribe($connection); + + /* + * Unset all channels that have no connections so we don't leak memory. + */ + collect(array_get($this->channels, $connection->app->id, [])) + ->reject->hasConnections() + ->each(function (Channel $channel, string $channelName) use ($connection) { + unset($this->channels[$connection->app->id][$channelName]); + }); + + if (count(array_get($this->channels, $connection->app->id, [])) === 0) { + unset($this->channels[$connection->app->id]); + } + } +} diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index 3c821260a4..afacde5d6b 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -12,6 +12,7 @@ use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowDashboard; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\AuthenticateDashboard; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\DashboardApiController; +use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager; use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize as AuthorizeDashboard; use BeyondCode\LaravelWebSockets\Statistics\Http\Middleware\Authorize as AuthorizeStatistics; use BeyondCode\LaravelWebSockets\Statistics\Http\Controllers\WebSocketStatisticsEntriesController; @@ -51,7 +52,8 @@ public function register() }); $this->app->singleton(ChannelManager::class, function () { - return new ChannelManager(); + return config('websockets.channel_manager') !== null && class_exists(config('websockets.channel_manager')) + ? app(config('websockets.channel_manager')) : new ArrayChannelManager(); }); $this->app->singleton(AppProvider::class, function () {