Update ChannelManager (#41)

* Update Channel Manager

Switch ChannelManager to an interface. Extend with an ArrayChannelManager (and copy the code). Update the config to allow for swapping. Bind it to the ServiceProvider

* Add a fallback if the config variable isn’t set

* Fix StyleCI

* Fix StyleCI line break

* Add description for the ChannelManager option
This commit is contained in:
Sam Snelling 2018-12-10 13:47:52 -06:00 committed by Marcel Pociot
parent db6d794fd5
commit ec96ca7172
4 changed files with 103 additions and 70 deletions

View File

@ -97,4 +97,13 @@ return [
*/
'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,
];

View File

@ -4,78 +4,15 @@ namespace BeyondCode\LaravelWebSockets\WebSockets\Channels;
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);
}

View File

@ -0,0 +1,85 @@
<?php
namespace BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers;
use Ratchet\ConnectionInterface;
use BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel;
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
use BeyondCode\LaravelWebSockets\WebSockets\Channels\PrivateChannel;
use BeyondCode\LaravelWebSockets\WebSockets\Channels\PresenceChannel;
class ArrayChannelManager implements ChannelManager
{
/** @var string */
protected $appId;
/** @var array */
protected $channels = [];
public function findOrCreate(string $appId, string $channelName): Channel
{
if (! isset($this->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]);
}
}
}

View File

@ -12,6 +12,7 @@ use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\SendMessage;
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 @@ class WebSocketsServiceProvider extends ServiceProvider
});
$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 () {