From 0b412cd98ec85e09b6d82d8a4119f66350975bc1 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Tue, 18 Aug 2020 20:21:22 +0300 Subject: [PATCH 1/2] added docblocks --- docs/advanced-usage/app-providers.md | 8 +- src/Apps/App.php | 77 +++++++- src/Apps/AppManager.php | 28 ++- src/Apps/ConfigAppManager.php | 45 ++++- src/Console/CleanStatistics.php | 26 ++- src/Console/RestartWebSocketServer.php | 15 ++ src/Console/StartWebSocketServer.php | 132 +++++++++++--- src/Dashboard/DashboardLogger.php | 8 + .../Controllers/AuthenticateDashboard.php | 13 +- .../Controllers/DashboardApiController.php | 14 +- .../Http/Controllers/SendMessage.php | 24 ++- .../Http/Controllers/ShowDashboard.php | 7 + src/Dashboard/Http/Middleware/Authorize.php | 11 +- src/Exceptions/InvalidApp.php | 18 ++ src/Exceptions/InvalidWebSocketController.php | 15 +- src/Facades/StatisticsLogger.php | 5 + src/Facades/WebSocketsRouter.php | 7 +- src/HttpApi/Controllers/Controller.php | 166 ++++++++++++++---- .../Controllers/FetchChannelController.php | 6 + .../Controllers/FetchChannelsController.php | 19 +- .../Controllers/FetchUsersController.php | 6 + .../Controllers/TriggerEventController.php | 6 + .../Broadcasters/RedisPusherBroadcaster.php | 4 +- src/PubSub/Drivers/LocalClient.php | 14 +- src/PubSub/Drivers/RedisClient.php | 14 +- src/PubSub/ReplicationInterface.php | 14 +- src/QueryParameters.php | 23 ++- src/Server/HttpServer.php | 7 + src/Server/Logger/ConnectionLogger.php | 46 ++++- src/Server/Logger/HttpLogger.php | 44 ++++- src/Server/Logger/Logger.php | 65 ++++++- src/Server/Logger/WebsocketsLogger.php | 44 ++++- src/Server/Router.php | 97 +++++++++- src/Server/WebSocketServerFactory.php | 79 ++++++--- src/Statistics/DnsResolver.php | 40 +++-- src/Statistics/Events/StatisticsUpdated.php | 31 +++- .../WebSocketStatisticsEntriesController.php | 6 + src/Statistics/Http/Middleware/Authorize.php | 11 +- .../Logger/HttpStatisticsLogger.php | 95 +++++++--- .../Logger/NullStatisticsLogger.php | 48 ++++- src/Statistics/Logger/StatisticsLogger.php | 29 +++ .../Models/WebSocketsStatisticsEntry.php | 6 + src/Statistics/Rules/AppId.php | 12 ++ src/Statistics/Statistic.php | 72 +++++++- src/WebSockets/Channels/Channel.php | 99 ++++++++++- src/WebSockets/Channels/ChannelManager.php | 40 ++++- .../ChannelManagers/ArrayChannelManager.php | 99 ++++++++--- src/WebSockets/Channels/PresenceChannel.php | 4 +- src/WebSockets/Channels/PrivateChannel.php | 6 + .../Exceptions/ConnectionsOverCapacity.php | 10 +- .../Exceptions/InvalidConnection.php | 7 +- .../Exceptions/InvalidSignature.php | 7 +- .../Exceptions/OriginNotAllowed.php | 6 + src/WebSockets/Exceptions/UnknownAppKey.php | 2 +- .../Exceptions/WebSocketException.php | 5 + .../Messages/PusherChannelProtocolMessage.php | 54 +++++- .../Messages/PusherClientMessage.php | 32 +++- src/WebSockets/Messages/PusherMessage.php | 5 + .../Messages/PusherMessageFactory.php | 8 + src/WebSockets/WebSocketHandler.php | 71 +++++++- src/WebSocketsServiceProvider.php | 68 ++++--- tests/ClientProviders/AppTest.php | 2 +- tests/HttpApi/FetchUsersReplicationTest.php | 8 +- tests/TestCase.php | 64 ++++++- 64 files changed, 1743 insertions(+), 311 deletions(-) diff --git a/docs/advanced-usage/app-providers.md b/docs/advanced-usage/app-providers.md index 1cdeba7235..aca721dbe3 100644 --- a/docs/advanced-usage/app-providers.md +++ b/docs/advanced-usage/app-providers.md @@ -25,10 +25,10 @@ interface AppManager public function findById($appId): ?App; /** @return BeyondCode\LaravelWebSockets\Apps\App */ - public function findByKey(string $appKey): ?App; + public function findByKey($appKey): ?App; /** @return BeyondCode\LaravelWebSockets\Apps\App */ - public function findBySecret(string $appSecret): ?App; + public function findBySecret($appSecret): ?App; } ``` @@ -56,12 +56,12 @@ class MyCustomAppManager implements AppManager return $this->normalize(Application::findById($appId)->toArray()); } - public function findByKey(string $appKey) : ? App + public function findByKey($appKey) : ? App { return $this->normalize(Application::findByKey($appKey)->toArray()); } - public function findBySecret(string $appSecret) : ? App + public function findBySecret($appSecret) : ? App { return $this->normalize(Application::findBySecret($appSecret)->toArray()); } diff --git a/src/Apps/App.php b/src/Apps/App.php index 8844079465..ae23f4d687 100644 --- a/src/Apps/App.php +++ b/src/Apps/App.php @@ -36,22 +36,49 @@ class App /** @var array */ public $allowedOrigins = []; + /** + * Find the app by id. + * + * @param mixed $appId + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ public static function findById($appId) { return app(AppManager::class)->findById($appId); } - public static function findByKey(string $appKey): ?self + /** + * Find the app by app key. + * + * @param mixed $appId + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ + public static function findByKey($appKey): ?self { return app(AppManager::class)->findByKey($appKey); } - public static function findBySecret(string $appSecret): ?self + /** + * Find the app by app secret. + * + * @param mixed $appId + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ + public static function findBySecret($appSecret): ?self { return app(AppManager::class)->findBySecret($appSecret); } - public function __construct($appId, string $appKey, string $appSecret) + /** + * Initialize the Web Socket app instance. + * + * @param mixed $appId + * @param mixed $key + * @param mixed $secret + * @return void + * @throws \BeyondCode\LaravelWebSockets\Exceptions\InvalidApp + */ + public function __construct($appId, $appKey, $appSecret) { if ($appKey === '') { throw InvalidApp::valueIsRequired('appKey', $appId); @@ -62,12 +89,16 @@ public function __construct($appId, string $appKey, string $appSecret) } $this->id = $appId; - $this->key = $appKey; - $this->secret = $appSecret; } + /** + * Set the name of the app. + * + * @param string $appName + * @return $this + */ public function setName(string $appName) { $this->name = $appName; @@ -75,6 +106,12 @@ public function setName(string $appName) return $this; } + /** + * Set the app host. + * + * @param string $host + * @return $this + */ public function setHost(string $host) { $this->host = $host; @@ -82,6 +119,12 @@ public function setHost(string $host) return $this; } + /** + * Set path for the app. + * + * @param string $path + * @return $this + */ public function setPath(string $path) { $this->path = $path; @@ -89,6 +132,12 @@ public function setPath(string $path) return $this; } + /** + * Enable client messages. + * + * @param bool $enabled + * @return $this + */ public function enableClientMessages(bool $enabled = true) { $this->clientMessagesEnabled = $enabled; @@ -96,6 +145,12 @@ public function enableClientMessages(bool $enabled = true) return $this; } + /** + * Set the maximum capacity for the app. + * + * @param int|null $capacity + * @return $this + */ public function setCapacity(?int $capacity) { $this->capacity = $capacity; @@ -103,6 +158,12 @@ public function setCapacity(?int $capacity) return $this; } + /** + * Enable statistics for the app. + * + * @param bool $enabled + * @return $this + */ public function enableStatistics(bool $enabled = true) { $this->statisticsEnabled = $enabled; @@ -110,6 +171,12 @@ public function enableStatistics(bool $enabled = true) return $this; } + /** + * Add whitelisted origins. + * + * @param array $allowedOrigins + * @return $this + */ public function setAllowedOrigins(array $allowedOrigins) { $this->allowedOrigins = $allowedOrigins; diff --git a/src/Apps/AppManager.php b/src/Apps/AppManager.php index ff63a31599..ef8cb860be 100644 --- a/src/Apps/AppManager.php +++ b/src/Apps/AppManager.php @@ -4,12 +4,34 @@ interface AppManager { - /** @return array[BeyondCode\LaravelWebSockets\Apps\App] */ + /** + * Get all apps. + * + * @return array[\BeyondCode\LaravelWebSockets\Apps\App] + */ public function all(): array; + /** + * Get app by id. + * + * @param mixed $appId + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ public function findById($appId): ?App; - public function findByKey(string $appKey): ?App; + /** + * Get app by app key. + * + * @param mixed $appKey + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ + public function findByKey($appKey): ?App; - public function findBySecret(string $appSecret): ?App; + /** + * Get app by secret. + * + * @param mixed $appSecret + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ + public function findBySecret($appSecret): ?App; } diff --git a/src/Apps/ConfigAppManager.php b/src/Apps/ConfigAppManager.php index 235d89affe..c029d71b2b 100644 --- a/src/Apps/ConfigAppManager.php +++ b/src/Apps/ConfigAppManager.php @@ -6,15 +6,28 @@ class ConfigAppManager implements AppManager { - /** @var Collection */ + /** + * The list of apps. + * + * @var \Illuminate\Support\Collection + */ protected $apps; + /** + * Initialize the class. + * + * @return void + */ public function __construct() { $this->apps = collect(config('websockets.apps')); } - /** @return array[\BeyondCode\LaravelWebSockets\Apps\App] */ + /** + * Get all apps. + * + * @return array[\BeyondCode\LaravelWebSockets\Apps\App] + */ public function all(): array { return $this->apps @@ -24,6 +37,12 @@ public function all(): array ->toArray(); } + /** + * Get app by id. + * + * @param mixed $appId + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ public function findById($appId): ?App { $appAttributes = $this @@ -33,7 +52,13 @@ public function findById($appId): ?App return $this->instantiate($appAttributes); } - public function findByKey(string $appKey): ?App + /** + * Get app by app key. + * + * @param mixed $appKey + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ + public function findByKey($appKey): ?App { $appAttributes = $this ->apps @@ -42,7 +67,13 @@ public function findByKey(string $appKey): ?App return $this->instantiate($appAttributes); } - public function findBySecret(string $appSecret): ?App + /** + * Get app by secret. + * + * @param mixed $appSecret + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ + public function findBySecret($appSecret): ?App { $appAttributes = $this ->apps @@ -51,6 +82,12 @@ public function findBySecret(string $appSecret): ?App return $this->instantiate($appAttributes); } + /** + * Map the app into an App instance. + * + * @param array|null $app + * @return \BeyondCode\LaravelWebSockets\Apps\App|null + */ protected function instantiate(?array $appAttributes): ?App { if (! $appAttributes) { diff --git a/src/Console/CleanStatistics.php b/src/Console/CleanStatistics.php index aba815f51a..786ff37c24 100644 --- a/src/Console/CleanStatistics.php +++ b/src/Console/CleanStatistics.php @@ -8,11 +8,27 @@ class CleanStatistics extends Command { + /** + * The name and signature of the console command. + * + * @var string + */ protected $signature = 'websockets:clean - {appId? : (optional) The app id that will be cleaned.}'; - + {appId? : (optional) The app id that will be cleaned.} + '; + + /** + * The console command description. + * + * @var string|null + */ protected $description = 'Clean up old statistics from the websocket log.'; + /** + * Run the command. + * + * @return void + */ public function handle() { $this->comment('Cleaning WebSocket Statistics...'); @@ -23,16 +39,14 @@ public function handle() $cutOffDate = Carbon::now()->subDay($maxAgeInDays)->format('Y-m-d H:i:s'); - $webSocketsStatisticsEntryModelClass = config('websockets.statistics.model'); + $class = config('websockets.statistics.model'); - $amountDeleted = $webSocketsStatisticsEntryModelClass::where('created_at', '<', $cutOffDate) + $amountDeleted = $class::where('created_at', '<', $cutOffDate) ->when(! is_null($appId), function (Builder $query) use ($appId) { $query->where('app_id', $appId); }) ->delete(); $this->info("Deleted {$amountDeleted} record(s) from the WebSocket statistics."); - - $this->comment('All done!'); } } diff --git a/src/Console/RestartWebSocketServer.php b/src/Console/RestartWebSocketServer.php index 26d240cb76..eac1b65a39 100644 --- a/src/Console/RestartWebSocketServer.php +++ b/src/Console/RestartWebSocketServer.php @@ -10,10 +10,25 @@ class RestartWebSocketServer extends Command { use InteractsWithTime; + /** + * The name and signature of the console command. + * + * @var string + */ protected $signature = 'websockets:restart'; + /** + * The console command description. + * + * @var string|null + */ protected $description = 'Restart the Laravel WebSocket Server'; + /** + * Run the command. + * + * @return void + */ public function handle() { Cache::forever('beyondcode:websockets:restart', $this->currentTime()); diff --git a/src/Console/StartWebSocketServer.php b/src/Console/StartWebSocketServer.php index ee4b94e460..e6d047f1ae 100644 --- a/src/Console/StartWebSocketServer.php +++ b/src/Console/StartWebSocketServer.php @@ -25,6 +25,11 @@ class StartWebSocketServer extends Command { + /** + * The name and signature of the console command. + * + * @var string + */ protected $signature = 'websockets:serve {--host=0.0.0.0} {--port=6001} @@ -32,14 +37,39 @@ class StartWebSocketServer extends Command {--test : Prepare the server, but do not start it.} '; + /** + * The console command description. + * + * @var string|null + */ protected $description = 'Start the Laravel WebSocket Server'; - /** @var \React\EventLoop\LoopInterface */ + /** + * Get the loop instance. + * + * @var \React\EventLoop\LoopInterface + */ protected $loop; - /** @var int */ + /** + * The Pusher server instance. + * + * @var \Ratchet\Server\IoServer + */ + public $server; + + /** + * Track the last restart. + * + * @var int + */ protected $lastRestart; + /** + * Initialize the command. + * + * @return void + */ public function __construct() { parent::__construct(); @@ -47,6 +77,11 @@ public function __construct() $this->loop = LoopFactory::create(); } + /** + * Run the command. + * + * @return void + */ public function handle() { $this @@ -56,12 +91,15 @@ public function handle() ->configureConnectionLogger() ->configureRestartTimer() ->configurePubSub() - ->registerEchoRoutes() - ->registerCustomRoutes() - ->configurePubSubReplication() + ->registerRoutes() ->startWebSocketServer(); } + /** + * Configure the statistics logger class. + * + * @return $this + */ protected function configureStatisticsLogger() { $connector = new Connector($this->loop, [ @@ -87,6 +125,11 @@ protected function configureStatisticsLogger() return $this; } + /** + * Configure the HTTP logger class. + * + * @return $this + */ protected function configureHttpLogger() { $this->laravel->singleton(HttpLogger::class, function () { @@ -98,6 +141,11 @@ protected function configureHttpLogger() return $this; } + /** + * Configure the logger for messages. + * + * @return $this + */ protected function configureMessageLogger() { $this->laravel->singleton(WebsocketsLogger::class, function () { @@ -109,6 +157,11 @@ protected function configureMessageLogger() return $this; } + /** + * Configure the connection logger. + * + * @return $this + */ protected function configureConnectionLogger() { $this->laravel->bind(ConnectionLogger::class, function () { @@ -120,6 +173,11 @@ protected function configureConnectionLogger() return $this; } + /** + * Configure the Redis PubSub handler. + * + * @return $this + */ public function configureRestartTimer() { $this->lastRestart = $this->getLastRestart(); @@ -152,52 +210,65 @@ public function configurePubSub() }); } - return $this; - } - - protected function registerEchoRoutes() - { - WebSocketsRouter::echo(); + $this->laravel + ->get(ReplicationInterface::class) + ->boot($this->loop); return $this; } - protected function registerCustomRoutes() + /** + * Register the routes. + * + * @return $this + */ + protected function registerRoutes() { - WebSocketsRouter::customRoutes(); + WebSocketsRouter::routes(); return $this; } + /** + * Start the server. + * + * @return void + */ protected function startWebSocketServer() { $this->info("Starting the WebSocket server on port {$this->option('port')}..."); - $routes = WebSocketsRouter::getRoutes(); - - $server = (new WebSocketServerFactory()) - ->setLoop($this->loop) - ->useRoutes($routes) - ->setHost($this->option('host')) - ->setPort($this->option('port')) - ->setConsoleOutput($this->output) - ->createServer(); + $this->buildServer(); if (! $this->option('test')) { /* 🛰 Start the server 🛰 */ - $server->run(); + $this->server->run(); } } - protected function configurePubSubReplication() + /** + * Build the server instance. + * + * @return void + */ + protected function buildServer() { - $this->laravel - ->get(ReplicationInterface::class) - ->boot($this->loop); + $this->server = new WebSocketServerFactory( + $this->option('host'), $this->option('port') + ); - return $this; + $this->server = $this->server + ->setLoop($this->loop) + ->useRoutes(WebSocketsRouter::getRoutes()) + ->setConsoleOutput($this->output) + ->createServer(); } + /** + * Create a DNS resolver for the stats manager. + * + * @return \React\Dns\Resolver\ResolverInterface + */ protected function getDnsResolver(): ResolverInterface { if (! config('websockets.statistics.perform_dns_lookup')) { @@ -214,6 +285,11 @@ protected function getDnsResolver(): ResolverInterface ); } + /** + * Get the last time the server restarted. + * + * @return int + */ protected function getLastRestart() { return Cache::get('beyondcode:websockets:restart', 0); diff --git a/src/Dashboard/DashboardLogger.php b/src/Dashboard/DashboardLogger.php index f5d0980873..70397cec83 100644 --- a/src/Dashboard/DashboardLogger.php +++ b/src/Dashboard/DashboardLogger.php @@ -55,6 +55,14 @@ class DashboardLogger self::TYPE_REPLICATOR_MESSAGE_RECEIVED, ]; + /** + * Log an event for an app. + * + * @param mixed $appId + * @param string $type + * @param array $details + * @return void + */ public static function log($appId, string $type, array $details = []) { $channelName = static::LOG_CHANNEL_PREFIX.$type; diff --git a/src/Dashboard/Http/Controllers/AuthenticateDashboard.php b/src/Dashboard/Http/Controllers/AuthenticateDashboard.php index 0f9f56c388..68ed43b096 100644 --- a/src/Dashboard/Http/Controllers/AuthenticateDashboard.php +++ b/src/Dashboard/Http/Controllers/AuthenticateDashboard.php @@ -9,13 +9,16 @@ class AuthenticateDashboard { + /** + * Find the app by using the header + * and then reconstruct the PusherBroadcaster + * using our own app selection. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ public function __invoke(Request $request) { - /** - * Find the app by using the header - * and then reconstruct the PusherBroadcaster - * using our own app selection. - */ $app = App::findById($request->header('x-app-id')); $broadcaster = new PusherBroadcaster(new Pusher( diff --git a/src/Dashboard/Http/Controllers/DashboardApiController.php b/src/Dashboard/Http/Controllers/DashboardApiController.php index 1d77b9d874..1e63fb9417 100644 --- a/src/Dashboard/Http/Controllers/DashboardApiController.php +++ b/src/Dashboard/Http/Controllers/DashboardApiController.php @@ -4,10 +4,20 @@ class DashboardApiController { + /** + * Get statistics for an app ID. + * + * @param mixed $appId + * @return \Illuminate\Http\Response + */ public function getStatistics($appId) { - $webSocketsStatisticsEntryModelClass = config('websockets.statistics.model'); - $statistics = $webSocketsStatisticsEntryModelClass::where('app_id', $appId)->latest()->limit(120)->get(); + $model = config('websockets.statistics.model'); + + $statistics = $model::where('app_id', $appId) + ->latest() + ->limit(120) + ->get(); $statisticData = $statistics->map(function ($statistic) { return [ diff --git a/src/Dashboard/Http/Controllers/SendMessage.php b/src/Dashboard/Http/Controllers/SendMessage.php index fe0c75557d..92777e4a0f 100644 --- a/src/Dashboard/Http/Controllers/SendMessage.php +++ b/src/Dashboard/Http/Controllers/SendMessage.php @@ -9,15 +9,21 @@ class SendMessage { + /** + * Send the message to the requested channel. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ public function __invoke(Request $request) { $validated = $request->validate([ - 'appId' => ['required', new AppId()], - 'key' => 'required', - 'secret' => 'required', - 'channel' => 'required', - 'event' => 'required', - 'data' => 'json', + 'appId' => ['required', new AppId], + 'key' => 'required|string', + 'secret' => 'required|string', + 'channel' => 'required|string', + 'event' => 'required|string', + 'data' => 'required|json', ]); $this->getPusherBroadcaster($validated)->broadcast( @@ -29,6 +35,12 @@ public function __invoke(Request $request) return 'ok'; } + /** + * Get the pusher broadcaster for the current request. + * + * @param array $validated + * @return \Illuminate\Broadcasting\Broadcasters\PusherBroadcaster + */ protected function getPusherBroadcaster(array $validated): PusherBroadcaster { $pusher = new Pusher( diff --git a/src/Dashboard/Http/Controllers/ShowDashboard.php b/src/Dashboard/Http/Controllers/ShowDashboard.php index 7f22a45dd3..8ce4208e8d 100644 --- a/src/Dashboard/Http/Controllers/ShowDashboard.php +++ b/src/Dashboard/Http/Controllers/ShowDashboard.php @@ -8,6 +8,13 @@ class ShowDashboard { + /** + * Show the dashboard. + * + * @param \Illuminate\Http\Request $request + * @param \BeyondCode\LaravelWebSockets\Apps\AppManager $apps + * @return void + */ public function __invoke(Request $request, AppManager $apps) { return view('websockets::dashboard', [ diff --git a/src/Dashboard/Http/Middleware/Authorize.php b/src/Dashboard/Http/Middleware/Authorize.php index 1883c35eef..5a16343be8 100644 --- a/src/Dashboard/Http/Middleware/Authorize.php +++ b/src/Dashboard/Http/Middleware/Authorize.php @@ -6,8 +6,17 @@ class Authorize { + /** + * Authorize the current user. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return \Illuminate\Http\Response + */ public function handle($request, $next) { - return Gate::check('viewWebSocketsDashboard', [$request->user()]) ? $next($request) : abort(403); + return Gate::check('viewWebSocketsDashboard', [$request->user()]) + ? $next($request) + : abort(403); } } diff --git a/src/Exceptions/InvalidApp.php b/src/Exceptions/InvalidApp.php index 28e50d94ad..2270ae004e 100644 --- a/src/Exceptions/InvalidApp.php +++ b/src/Exceptions/InvalidApp.php @@ -9,16 +9,34 @@ class InvalidApp extends Exception implements ProvidesSolution { + /** + * Throw an "app not found by id" exception. + * + * @param mixed $appId + * @return \BeyondCode\LaravelWebSockets\Exceptions\InvalidApp + */ public static function notFound($appId) { return new static("Could not find app for app id `{$appId}`."); } + /** + * Throw an "app id required" exception. + * + * @param string $name + * @param mixed $appId + * @return \BeyondCode\LaravelWebSockets\Exceptions\InvalidApp + */ public static function valueIsRequired($name, $appId) { return new static("{$name} is required but was empty for app id `{$appId}`."); } + /** + * Provide the solution for Igniter. + * + * @return \Facade\IgnitionContracts\BaseSolution + */ public function getSolution(): Solution { return BaseSolution::create('Your application id could not be found') diff --git a/src/Exceptions/InvalidWebSocketController.php b/src/Exceptions/InvalidWebSocketController.php index 96c1a4ae8b..f216e50651 100644 --- a/src/Exceptions/InvalidWebSocketController.php +++ b/src/Exceptions/InvalidWebSocketController.php @@ -2,14 +2,23 @@ namespace BeyondCode\LaravelWebSockets\Exceptions; +use Exception; use Ratchet\WebSocket\MessageComponentInterface; -class InvalidWebSocketController extends \Exception +class InvalidWebSocketController extends Exception { + /** + * Allocate a controller to the error. + * + * @param string $controllerClass + * @return \BeyondCode\LaravelWebSockets\Exceptions\InvalidWebSocketController + */ public static function withController(string $controllerClass) { - $messageComponentInterfaceClass = MessageComponentInterface::class; + $class = MessageComponentInterface::class; - return new static("Invalid WebSocket Controller provided. Expected instance of `{$messageComponentInterfaceClass}`, but received `{$controllerClass}`."); + return new static( + "Invalid WebSocket Controller provided. Expected instance of `{$class}`, but received `{$controllerClass}`." + ); } } diff --git a/src/Facades/StatisticsLogger.php b/src/Facades/StatisticsLogger.php index 518334279a..59e58d9df7 100644 --- a/src/Facades/StatisticsLogger.php +++ b/src/Facades/StatisticsLogger.php @@ -11,6 +11,11 @@ */ class StatisticsLogger extends Facade { + /** + * Get the registered name of the component. + * + * @return string + */ protected static function getFacadeAccessor() { return StatisticsLoggerInterface::class; diff --git a/src/Facades/WebSocketsRouter.php b/src/Facades/WebSocketsRouter.php index 925f6856e7..94e8d0a0bb 100644 --- a/src/Facades/WebSocketsRouter.php +++ b/src/Facades/WebSocketsRouter.php @@ -5,11 +5,16 @@ use Illuminate\Support\Facades\Facade; /** - * @see \BeyondCode\LaravelWebSockets\Server\Router + * @see \BeyondCode\LaravelWebSockets\Server\Router * @mixin \BeyondCode\LaravelWebSockets\Server\Router */ class WebSocketsRouter extends Facade { + /** + * Get the registered name of the component. + * + * @return string + */ protected static function getFacadeAccessor() { return 'websockets.router'; diff --git a/src/HttpApi/Controllers/Controller.php b/src/HttpApi/Controllers/Controller.php index 6e8b449f50..437accc3d2 100644 --- a/src/HttpApi/Controllers/Controller.php +++ b/src/HttpApi/Controllers/Controller.php @@ -22,23 +22,53 @@ abstract class Controller implements HttpServerInterface { - /** @var string */ + /** + * The request buffer. + * + * @var string + */ protected $requestBuffer = ''; - /** @var RequestInterface */ + /** + * The incoming request. + * + * @var \Psr\Http\Message\RequestInterface + */ protected $request; - /** @var int */ + /** + * The content length that will + * be calculated. + * + * @var int + */ protected $contentLength; - /** @var ChannelManager */ + /** + * The channel manager. + * + * @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager + */ protected $channelManager; + /** + * Initialize the request. + * + * @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager + * @return void + */ public function __construct(ChannelManager $channelManager) { $this->channelManager = $channelManager; } + /** + * Handle the opened socket connection. + * + * @param \Ratchet\ConnectionInterface $connection + * @param \Psr\Http\Message\RequestInterface $request + * @return void + */ public function onOpen(ConnectionInterface $connection, RequestInterface $request = null) { $this->request = $request; @@ -54,13 +84,13 @@ public function onOpen(ConnectionInterface $connection, RequestInterface $reques $this->handleRequest($connection); } - protected function findContentLength(array $headers): int - { - return Collection::make($headers)->first(function ($values, $header) { - return strtolower($header) === 'content-length'; - })[0] ?? 0; - } - + /** + * Handle the oncoming message and add it to buffer. + * + * @param \Ratchet\ConnectionInterface $from + * @param mixed $msg + * @return void + */ public function onMessage(ConnectionInterface $from, $msg) { $this->requestBuffer .= $msg; @@ -72,11 +102,70 @@ public function onMessage(ConnectionInterface $from, $msg) $this->handleRequest($from); } + /** + * Handle the socket closing. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ + public function onClose(ConnectionInterface $connection) + { + // + } + + /** + * Handle the errors. + * + * @param \Ratchet\ConnectionInterface $connection + * @param Exception $exception + * @return void + */ + public function onError(ConnectionInterface $connection, Exception $exception) + { + if (! $exception instanceof HttpException) { + return; + } + + $response = new Response($exception->getStatusCode(), [ + 'Content-Type' => 'application/json', + ], json_encode([ + 'error' => $exception->getMessage(), + ])); + + $connection->send(\GuzzleHttp\Psr7\str($response)); + + $connection->close(); + } + + /** + * Get the content length from the headers. + * + * @param array $headers + * @return int + */ + protected function findContentLength(array $headers): int + { + return Collection::make($headers)->first(function ($values, $header) { + return strtolower($header) === 'content-length'; + })[0] ?? 0; + } + + /** + * Check the content length. + * + * @return bool + */ protected function verifyContentLength() { return strlen($this->requestBuffer) === $this->contentLength; } + /** + * Handle the oncoming connection. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ protected function handleRequest(ConnectionInterface $connection) { $serverRequest = (new ServerRequest( @@ -108,34 +197,26 @@ protected function handleRequest(ConnectionInterface $connection) $this->sendAndClose($connection, $response); } + /** + * Send the response and close the connection. + * + * @param \Ratchet\ConnectionInterface $connection + * @param mixed $response + * @return void + */ protected function sendAndClose(ConnectionInterface $connection, $response) { - $connection->send(JsonResponse::create($response)); - $connection->close(); - } - - public function onClose(ConnectionInterface $connection) - { - } - - public function onError(ConnectionInterface $connection, Exception $exception) - { - if (! $exception instanceof HttpException) { - return; - } - - $response = new Response($exception->getStatusCode(), [ - 'Content-Type' => 'application/json', - ], json_encode([ - 'error' => $exception->getMessage(), - ])); - - $connection->send(\GuzzleHttp\Psr7\str($response)); - - $connection->close(); + tap($connection)->send(JsonResponse::create($response))->close(); } - public function ensureValidAppId(string $appId) + /** + * Ensure app existence. + * + * @param mixed $appId + * @return $this + * @throws \Symfony\Component\HttpKernel\Exception\HttpException + */ + public function ensureValidAppId($appId) { if (! App::findById($appId)) { throw new HttpException(401, "Unknown app id `{$appId}` provided."); @@ -144,11 +225,18 @@ public function ensureValidAppId(string $appId) return $this; } + /** + * Ensure signature integrity coming from an + * authorized application. + * + * @param \GuzzleHttp\Psr7\ServerRequest $request + * @return $this + * @throws \Symfony\Component\HttpKernel\Exception\HttpException + */ protected function ensureValidSignature(Request $request) { /* * The `auth_signature` & `body_md5` parameters are not included when calculating the `auth_signature` value. - * * The `appId`, `appKey` & `channelName` parameters are actually route parameters and are never supplied by the client. */ $params = Arr::except($request->query(), ['auth_signature', 'body_md5', 'appId', 'appKey', 'channelName']); @@ -170,5 +258,11 @@ protected function ensureValidSignature(Request $request) return $this; } + /** + * Handle the incoming request. + * + * @param \Illuminate\Http\Request $request + * @return void + */ abstract public function __invoke(Request $request); } diff --git a/src/HttpApi/Controllers/FetchChannelController.php b/src/HttpApi/Controllers/FetchChannelController.php index 188e08cc4e..a605ccf2fd 100644 --- a/src/HttpApi/Controllers/FetchChannelController.php +++ b/src/HttpApi/Controllers/FetchChannelController.php @@ -7,6 +7,12 @@ class FetchChannelController extends Controller { + /** + * Handle the incoming request. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ public function __invoke(Request $request) { $channel = $this->channelManager->find($request->appId, $request->channelName); diff --git a/src/HttpApi/Controllers/FetchChannelsController.php b/src/HttpApi/Controllers/FetchChannelsController.php index a1a06e1095..960a0db000 100644 --- a/src/HttpApi/Controllers/FetchChannelsController.php +++ b/src/HttpApi/Controllers/FetchChannelsController.php @@ -13,9 +13,20 @@ class FetchChannelsController extends Controller { - /** @var ReplicationInterface */ + /** + * The replicator driver. + * + * @var \BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface + */ protected $replicator; + /** + * Initialize the class. + * + * @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager + * @param \BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface $replicator + * @return void + */ public function __construct(ChannelManager $channelManager, ReplicationInterface $replicator) { parent::__construct($channelManager); @@ -23,6 +34,12 @@ public function __construct(ChannelManager $channelManager, ReplicationInterface $this->replicator = $replicator; } + /** + * Handle the incoming request. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ public function __invoke(Request $request) { $attributes = []; diff --git a/src/HttpApi/Controllers/FetchUsersController.php b/src/HttpApi/Controllers/FetchUsersController.php index efb712f5cd..25acee98c2 100644 --- a/src/HttpApi/Controllers/FetchUsersController.php +++ b/src/HttpApi/Controllers/FetchUsersController.php @@ -9,6 +9,12 @@ class FetchUsersController extends Controller { + /** + * Handle the incoming request. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ public function __invoke(Request $request) { $channel = $this->channelManager->find($request->appId, $request->channelName); diff --git a/src/HttpApi/Controllers/TriggerEventController.php b/src/HttpApi/Controllers/TriggerEventController.php index 819d417878..96c74878a9 100644 --- a/src/HttpApi/Controllers/TriggerEventController.php +++ b/src/HttpApi/Controllers/TriggerEventController.php @@ -8,6 +8,12 @@ class TriggerEventController extends Controller { + /** + * Handle the incoming request. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ public function __invoke(Request $request) { $this->ensureValidSignature($request); diff --git a/src/PubSub/Broadcasters/RedisPusherBroadcaster.php b/src/PubSub/Broadcasters/RedisPusherBroadcaster.php index 3476337388..c59f065bdf 100644 --- a/src/PubSub/Broadcasters/RedisPusherBroadcaster.php +++ b/src/PubSub/Broadcasters/RedisPusherBroadcaster.php @@ -46,11 +46,11 @@ class RedisPusherBroadcaster extends Broadcaster * Create a new broadcaster instance. * * @param Pusher $pusher - * @param string $appId + * @param $appId * @param \Illuminate\Contracts\Redis\Factory $redis * @param string|null $connection */ - public function __construct(Pusher $pusher, string $appId, Redis $redis, $connection = null) + public function __construct(Pusher $pusher, $appId, Redis $redis, $connection = null) { $this->pusher = $pusher; $this->appId = $appId; diff --git a/src/PubSub/Drivers/LocalClient.php b/src/PubSub/Drivers/LocalClient.php index 8209e83803..fe557158d8 100644 --- a/src/PubSub/Drivers/LocalClient.php +++ b/src/PubSub/Drivers/LocalClient.php @@ -37,7 +37,7 @@ public function boot(LoopInterface $loop, $factoryClass = null): ReplicationInte * @param stdClass $payload * @return bool */ - public function publish(string $appId, string $channel, stdClass $payload): bool + public function publish($appId, string $channel, stdClass $payload): bool { return true; } @@ -49,7 +49,7 @@ public function publish(string $appId, string $channel, stdClass $payload): bool * @param string $channel * @return bool */ - public function subscribe(string $appId, string $channel): bool + public function subscribe($appId, string $channel): bool { return true; } @@ -61,7 +61,7 @@ public function subscribe(string $appId, string $channel): bool * @param string $channel * @return bool */ - public function unsubscribe(string $appId, string $channel): bool + public function unsubscribe($appId, string $channel): bool { return true; } @@ -76,7 +76,7 @@ public function unsubscribe(string $appId, string $channel): bool * @param string $data * @return void */ - public function joinChannel(string $appId, string $channel, string $socketId, string $data) + public function joinChannel($appId, string $channel, string $socketId, string $data) { $this->channelData["{$appId}:{$channel}"][$socketId] = $data; } @@ -90,7 +90,7 @@ public function joinChannel(string $appId, string $channel, string $socketId, st * @param string $socketId * @return void */ - public function leaveChannel(string $appId, string $channel, string $socketId) + public function leaveChannel($appId, string $channel, string $socketId) { unset($this->channelData["{$appId}:{$channel}"][$socketId]); @@ -106,7 +106,7 @@ public function leaveChannel(string $appId, string $channel, string $socketId) * @param string $channel * @return PromiseInterface */ - public function channelMembers(string $appId, string $channel): PromiseInterface + public function channelMembers($appId, string $channel): PromiseInterface { $members = $this->channelData["{$appId}:{$channel}"] ?? []; @@ -124,7 +124,7 @@ public function channelMembers(string $appId, string $channel): PromiseInterface * @param array $channelNames * @return PromiseInterface */ - public function channelMemberCounts(string $appId, array $channelNames): PromiseInterface + public function channelMemberCounts($appId, array $channelNames): PromiseInterface { $results = []; diff --git a/src/PubSub/Drivers/RedisClient.php b/src/PubSub/Drivers/RedisClient.php index 11a479edd6..022faa8a41 100644 --- a/src/PubSub/Drivers/RedisClient.php +++ b/src/PubSub/Drivers/RedisClient.php @@ -97,7 +97,7 @@ public function boot(LoopInterface $loop, $factoryClass = null): ReplicationInte * @param stdClass $payload * @return bool */ - public function publish(string $appId, string $channel, stdClass $payload): bool + public function publish($appId, string $channel, stdClass $payload): bool { $payload->appId = $appId; $payload->serverId = $this->getServerId(); @@ -123,7 +123,7 @@ public function publish(string $appId, string $channel, stdClass $payload): bool * @param string $channel * @return bool */ - public function subscribe(string $appId, string $channel): bool + public function subscribe($appId, string $channel): bool { if (! isset($this->subscribedChannels["{$appId}:{$channel}"])) { // We're not subscribed to the channel yet, subscribe and set the count to 1 @@ -150,7 +150,7 @@ public function subscribe(string $appId, string $channel): bool * @param string $channel * @return bool */ - public function unsubscribe(string $appId, string $channel): bool + public function unsubscribe($appId, string $channel): bool { if (! isset($this->subscribedChannels["{$appId}:{$channel}"])) { return false; @@ -185,7 +185,7 @@ public function unsubscribe(string $appId, string $channel): bool * @param string $data * @return void */ - public function joinChannel(string $appId, string $channel, string $socketId, string $data) + public function joinChannel($appId, string $channel, string $socketId, string $data) { $this->publishClient->__call('hset', ["{$appId}:{$channel}", $socketId, $data]); @@ -207,7 +207,7 @@ public function joinChannel(string $appId, string $channel, string $socketId, st * @param string $socketId * @return void */ - public function leaveChannel(string $appId, string $channel, string $socketId) + public function leaveChannel($appId, string $channel, string $socketId) { $this->publishClient->__call('hdel', ["{$appId}:{$channel}", $socketId]); @@ -226,7 +226,7 @@ public function leaveChannel(string $appId, string $channel, string $socketId) * @param string $channel * @return PromiseInterface */ - public function channelMembers(string $appId, string $channel): PromiseInterface + public function channelMembers($appId, string $channel): PromiseInterface { return $this->publishClient->__call('hgetall', ["{$appId}:{$channel}"]) ->then(function ($members) { @@ -244,7 +244,7 @@ public function channelMembers(string $appId, string $channel): PromiseInterface * @param array $channelNames * @return PromiseInterface */ - public function channelMemberCounts(string $appId, array $channelNames): PromiseInterface + public function channelMemberCounts($appId, array $channelNames): PromiseInterface { $this->publishClient->__call('multi', []); diff --git a/src/PubSub/ReplicationInterface.php b/src/PubSub/ReplicationInterface.php index 71d83dd7c8..e0b39a86f0 100644 --- a/src/PubSub/ReplicationInterface.php +++ b/src/PubSub/ReplicationInterface.php @@ -25,7 +25,7 @@ public function boot(LoopInterface $loop, $factoryClass = null): self; * @param stdClass $payload * @return bool */ - public function publish(string $appId, string $channel, stdClass $payload): bool; + public function publish($appId, string $channel, stdClass $payload): bool; /** * Subscribe to receive messages for a channel. @@ -34,7 +34,7 @@ public function publish(string $appId, string $channel, stdClass $payload): bool * @param string $channel * @return bool */ - public function subscribe(string $appId, string $channel): bool; + public function subscribe($appId, string $channel): bool; /** * Unsubscribe from a channel. @@ -43,7 +43,7 @@ public function subscribe(string $appId, string $channel): bool; * @param string $channel * @return bool */ - public function unsubscribe(string $appId, string $channel): bool; + public function unsubscribe($appId, string $channel): bool; /** * Add a member to a channel. To be called when they have @@ -55,7 +55,7 @@ public function unsubscribe(string $appId, string $channel): bool; * @param string $data * @return void */ - public function joinChannel(string $appId, string $channel, string $socketId, string $data); + public function joinChannel($appId, string $channel, string $socketId, string $data); /** * Remove a member from the channel. To be called when they have @@ -66,7 +66,7 @@ public function joinChannel(string $appId, string $channel, string $socketId, st * @param string $socketId * @return void */ - public function leaveChannel(string $appId, string $channel, string $socketId); + public function leaveChannel($appId, string $channel, string $socketId); /** * Retrieve the full information about the members in a presence channel. @@ -75,7 +75,7 @@ public function leaveChannel(string $appId, string $channel, string $socketId); * @param string $channel * @return PromiseInterface */ - public function channelMembers(string $appId, string $channel): PromiseInterface; + public function channelMembers($appId, string $channel): PromiseInterface; /** * Get the amount of users subscribed for each presence channel. @@ -84,5 +84,5 @@ public function channelMembers(string $appId, string $channel): PromiseInterface * @param array $channelNames * @return PromiseInterface */ - public function channelMemberCounts(string $appId, array $channelNames): PromiseInterface; + public function channelMemberCounts($appId, array $channelNames): PromiseInterface; } diff --git a/src/QueryParameters.php b/src/QueryParameters.php index 85ee8af9a2..f0590e74e3 100644 --- a/src/QueryParameters.php +++ b/src/QueryParameters.php @@ -6,7 +6,11 @@ class QueryParameters { - /** @var \Psr\Http\Message\RequestInterface */ + /** + * The Request object. + * + * @var \Psr\Http\Message\RequestInterface + */ protected $request; public static function create(RequestInterface $request) @@ -14,11 +18,22 @@ public static function create(RequestInterface $request) return new static($request); } + /** + * Initialize the class. + * + * @param \Psr\Http\Message\RequestInterface $request + * @return void + */ public function __construct(RequestInterface $request) { $this->request = $request; } + /** + * Get all query parameters. + * + * @return array + */ public function all(): array { $queryParameters = []; @@ -28,6 +43,12 @@ public function all(): array return $queryParameters; } + /** + * Get a specific query parameter. + * + * @param string $name + * @return string + */ public function get(string $name): string { return $this->all()[$name] ?? ''; diff --git a/src/Server/HttpServer.php b/src/Server/HttpServer.php index 53cf1b79d6..b497d34403 100644 --- a/src/Server/HttpServer.php +++ b/src/Server/HttpServer.php @@ -6,6 +6,13 @@ class HttpServer extends \Ratchet\Http\HttpServer { + /** + * Create a new server instance. + * + * @param \Ratchet\Http\HttpServerInterface $component + * @param int $maxRequestSize + * @return void + */ public function __construct(HttpServerInterface $component, int $maxRequestSize = 4096) { parent::__construct($component); diff --git a/src/Server/Logger/ConnectionLogger.php b/src/Server/Logger/ConnectionLogger.php index 154c6c2588..e87c78c217 100644 --- a/src/Server/Logger/ConnectionLogger.php +++ b/src/Server/Logger/ConnectionLogger.php @@ -6,9 +6,19 @@ class ConnectionLogger extends Logger implements ConnectionInterface { - /** @var \Ratchet\ConnectionInterface */ + /** + * The connection to watch. + * + * @var \Ratchet\ConnectionInterface + */ protected $connection; + /** + * Create a new instance and add a connection to watch. + * + * @param \Ratchet\ConnectionInterface $connection + * @return Self + */ public static function decorate(ConnectionInterface $app): self { $logger = app(self::class); @@ -16,6 +26,12 @@ public static function decorate(ConnectionInterface $app): self return $logger->setConnection($app); } + /** + * Set a new connection to watch. + * + * @param \Ratchet\ConnectionInterface $connection + * @return $this + */ public function setConnection(ConnectionInterface $connection) { $this->connection = $connection; @@ -23,11 +39,12 @@ public function setConnection(ConnectionInterface $connection) return $this; } - protected function getConnection() - { - return $this->connection; - } - + /** + * Send data through the connection. + * + * @param mixed $data + * @return void + */ public function send($data) { $socketId = $this->connection->socketId ?? null; @@ -37,6 +54,11 @@ public function send($data) $this->connection->send($data); } + /** + * Close the connection. + * + * @return void + */ public function close() { $this->warn("Connection id {$this->connection->socketId} closing."); @@ -44,21 +66,33 @@ public function close() $this->connection->close(); } + /** + * {@inheritdoc} + */ public function __set($name, $value) { return $this->connection->$name = $value; } + /** + * {@inheritdoc} + */ public function __get($name) { return $this->connection->$name; } + /** + * {@inheritdoc} + */ public function __isset($name) { return isset($this->connection->$name); } + /** + * {@inheritdoc} + */ public function __unset($name) { unset($this->connection->$name); diff --git a/src/Server/Logger/HttpLogger.php b/src/Server/Logger/HttpLogger.php index b60b09983e..4ff6e12032 100644 --- a/src/Server/Logger/HttpLogger.php +++ b/src/Server/Logger/HttpLogger.php @@ -8,9 +8,19 @@ class HttpLogger extends Logger implements MessageComponentInterface { - /** @var \Ratchet\Http\HttpServerInterface */ + /** + * The HTTP app instance to watch. + * + * @var \Ratchet\Http\HttpServerInterface + */ protected $app; + /** + * Create a new instance and add the app to watch. + * + * @param \Ratchet\MessageComponentInterface $app + * @return Self + */ public static function decorate(MessageComponentInterface $app): self { $logger = app(self::class); @@ -18,6 +28,12 @@ public static function decorate(MessageComponentInterface $app): self return $logger->setApp($app); } + /** + * Set a new app to watch. + * + * @param \Ratchet\MessageComponentInterface $app + * @return $this + */ public function setApp(MessageComponentInterface $app) { $this->app = $app; @@ -25,21 +41,47 @@ public function setApp(MessageComponentInterface $app) return $this; } + /** + * Handle the HTTP open request. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function onOpen(ConnectionInterface $connection) { $this->app->onOpen($connection); } + /** + * Handle the HTTP message request. + * + * @param \Ratchet\ConnectionInterface $connection + * @param mixed $message + * @return void + */ public function onMessage(ConnectionInterface $connection, $message) { $this->app->onMessage($connection, $message); } + /** + * Handle the HTTP close request. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function onClose(ConnectionInterface $connection) { $this->app->onClose($connection); } + /** + * Handle HTTP errors. + * + * @param \Ratchet\ConnectionInterface $connection + * @param Exception $exception + * @return void + */ public function onError(ConnectionInterface $connection, Exception $exception) { $exceptionClass = get_class($exception); diff --git a/src/Server/Logger/Logger.php b/src/Server/Logger/Logger.php index ee62d4c908..5ad0ba7b6b 100644 --- a/src/Server/Logger/Logger.php +++ b/src/Server/Logger/Logger.php @@ -7,25 +7,54 @@ class Logger { - /** @var \Symfony\Component\Console\Output\OutputInterface */ + /** + * The console output interface. + * + * @var \Symfony\Component\Console\Output\OutputInterface + */ protected $consoleOutput; - /** @var bool */ + /** + * Wether the logger is enabled. + * + * @var bool + */ protected $enabled = false; - /** @var bool */ + /** + * Wether the verbose mode is on. + * + * @var bool + */ protected $verbose = false; + /** + * Check if the logger is active. + * + * @return bool + */ public static function isEnabled(): bool { return app(WebsocketsLogger::class)->enabled; } + /** + * Create a new Logger instance. + * + * @param \Symfony\Component\Console\Output\OutputInterface $consoleOutput + * @return void + */ public function __construct(OutputInterface $consoleOutput) { $this->consoleOutput = $consoleOutput; } + /** + * Enable the logger. + * + * @param bool $enabled + * @return $this + */ public function enable($enabled = true) { $this->enabled = $enabled; @@ -33,6 +62,12 @@ public function enable($enabled = true) return $this; } + /** + * Enable the verbose mode. + * + * @param bool $verbose + * @return $this + */ public function verbose($verbose = false) { $this->verbose = $verbose; @@ -40,11 +75,23 @@ public function verbose($verbose = false) return $this; } + /** + * Trigger an Info message. + * + * @param string $message + * @return void + */ protected function info(string $message) { $this->line($message, 'info'); } + /** + * Trigger a Warning message. + * + * @param string $message + * @return void + */ protected function warn(string $message) { if (! $this->consoleOutput->getFormatter()->hasStyle('warning')) { @@ -56,6 +103,12 @@ protected function warn(string $message) $this->line($message, 'warning'); } + /** + * Trigger an Error message. + * + * @param string $message + * @return void + */ protected function error(string $message) { $this->line($message, 'error'); @@ -63,8 +116,8 @@ protected function error(string $message) protected function line(string $message, string $style) { - $styled = $style ? "<$style>$message" : $message; - - $this->consoleOutput->writeln($styled); + $this->consoleOutput->writeln( + $style ? "<{$style}>{$message}" : $message + ); } } diff --git a/src/Server/Logger/WebsocketsLogger.php b/src/Server/Logger/WebsocketsLogger.php index 0279869000..7d600b148b 100644 --- a/src/Server/Logger/WebsocketsLogger.php +++ b/src/Server/Logger/WebsocketsLogger.php @@ -10,9 +10,19 @@ class WebsocketsLogger extends Logger implements MessageComponentInterface { - /** @var \Ratchet\Http\HttpServerInterface */ + /** + * The HTTP app instance to watch. + * + * @var \Ratchet\Http\HttpServerInterface + */ protected $app; + /** + * Create a new instance and add the app to watch. + * + * @param \Ratchet\MessageComponentInterface $app + * @return Self + */ public static function decorate(MessageComponentInterface $app): self { $logger = app(self::class); @@ -20,6 +30,12 @@ public static function decorate(MessageComponentInterface $app): self return $logger->setApp($app); } + /** + * Set a new app to watch. + * + * @param \Ratchet\MessageComponentInterface $app + * @return $this + */ public function setApp(MessageComponentInterface $app) { $this->app = $app; @@ -27,6 +43,12 @@ public function setApp(MessageComponentInterface $app) return $this; } + /** + * Handle the HTTP open request. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function onOpen(ConnectionInterface $connection) { $appKey = QueryParameters::create($connection->httpRequest)->get('appKey'); @@ -36,6 +58,13 @@ public function onOpen(ConnectionInterface $connection) $this->app->onOpen(ConnectionLogger::decorate($connection)); } + /** + * Handle the HTTP message request. + * + * @param \Ratchet\ConnectionInterface $connection + * @param \Ratchet\RFC6455\Messaging\MessageInterface $message + * @return void + */ public function onMessage(ConnectionInterface $connection, MessageInterface $message) { $this->info("{$connection->app->id}: connection id {$connection->socketId} received message: {$message->getPayload()}."); @@ -43,6 +72,12 @@ public function onMessage(ConnectionInterface $connection, MessageInterface $mes $this->app->onMessage(ConnectionLogger::decorate($connection), $message); } + /** + * Handle the HTTP close request. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function onClose(ConnectionInterface $connection) { $socketId = $connection->socketId ?? null; @@ -52,6 +87,13 @@ public function onClose(ConnectionInterface $connection) $this->app->onClose(ConnectionLogger::decorate($connection)); } + /** + * Handle HTTP errors. + * + * @param \Ratchet\ConnectionInterface $connection + * @param Exception $exception + * @return void + */ public function onError(ConnectionInterface $connection, Exception $exception) { $exceptionClass = get_class($exception); diff --git a/src/Server/Router.php b/src/Server/Router.php index bda51f174a..855c8e8fd2 100644 --- a/src/Server/Router.php +++ b/src/Server/Router.php @@ -17,22 +17,47 @@ class Router { - /** @var \Symfony\Component\Routing\RouteCollection */ + /** + * The implemented routes. + * + * @var \Symfony\Component\Routing\RouteCollection + */ protected $routes; + + /** + * The custom routes defined by the user. + * + * @var \Symfony\Component\Routing\RouteCollection + */ protected $customRoutes; + /** + * Initialize the class. + * + * @return void + */ public function __construct() { $this->routes = new RouteCollection; $this->customRoutes = new Collection(); } + /** + * Get the routes. + * + * @return \Symfony\Component\Routing\RouteCollection + */ public function getRoutes(): RouteCollection { return $this->routes; } - public function echo() + /** + * Register the routes. + * + * @return void + */ + public function routes() { $this->get('/app/{appKey}', config('websockets.handlers.websocket', WebSocketHandler::class)); @@ -40,40 +65,80 @@ public function echo() $this->get('/apps/{appId}/channels', FetchChannelsController::class); $this->get('/apps/{appId}/channels/{channelName}', FetchChannelController::class); $this->get('/apps/{appId}/channels/{channelName}/users', FetchUsersController::class); - } - public function customRoutes() - { $this->customRoutes->each(function ($action, $uri) { $this->get($uri, $action); }); } + /** + * Add a GET route. + * + * @param string $uri + * @param string $action + * @return void + */ public function get(string $uri, $action) { $this->addRoute('GET', $uri, $action); } + /** + * Add a POST route. + * + * @param string $uri + * @param string $action + * @return void + */ public function post(string $uri, $action) { $this->addRoute('POST', $uri, $action); } + /** + * Add a PUT route. + * + * @param string $uri + * @param string $action + * @return void + */ public function put(string $uri, $action) { $this->addRoute('PUT', $uri, $action); } + /** + * Add a PATCH route. + * + * @param string $uri + * @param string $action + * @return void + */ public function patch(string $uri, $action) { $this->addRoute('PATCH', $uri, $action); } + /** + * Add a DELETE route. + * + * @param string $uri + * @param string $action + * @return void + */ public function delete(string $uri, $action) { $this->addRoute('DELETE', $uri, $action); } + /** + * Add a WebSocket GET route that should + * comply with the MessageComponentInterface interface. + * + * @param string $uri + * @param string $action + * @return void + */ public function webSocket(string $uri, $action) { if (! is_subclass_of($action, MessageComponentInterface::class)) { @@ -83,11 +148,27 @@ public function webSocket(string $uri, $action) $this->customRoutes->put($uri, $action); } + /** + * Add a new route to the list. + * + * @param string $method + * @param string $uri + * @param string $action + * @return void + */ public function addRoute(string $method, string $uri, $action) { $this->routes->add($uri, $this->getRoute($method, $uri, $action)); } + /** + * Get the route of a specified method, uri and action. + * + * @param string $method + * @param string $uri + * @param string $action + * @return \Symfony\Component\Routing\Route + */ protected function getRoute(string $method, string $uri, $action): Route { /** @@ -103,6 +184,12 @@ protected function getRoute(string $method, string $uri, $action): Route return new Route($uri, ['_controller' => $action], [], [], null, [], [$method]); } + /** + * Create a new websockets server to handle the action. + * + * @param string $action + * @return \Ratchet\WebSocket\WsServer + */ protected function createWebSocketsServer(string $action): WsServer { $app = app($action); diff --git a/src/Server/WebSocketServerFactory.php b/src/Server/WebSocketServerFactory.php index bafeaa146a..163495aac7 100644 --- a/src/Server/WebSocketServerFactory.php +++ b/src/Server/WebSocketServerFactory.php @@ -16,26 +16,62 @@ class WebSocketServerFactory { - /** @var string */ + /** + * The host the server will run on. + * + * @var string + */ protected $host = '127.0.0.1'; - /** @var int */ + /** + * The port to run on. + * + * @var int + */ protected $port = 8080; - /** @var \React\EventLoop\LoopInterface */ + /** + * The event loop instance. + * + * @var \React\EventLoop\LoopInterface + */ protected $loop; - /** @var \Symfony\Component\Routing\RouteCollection */ + /** + * The routes to register. + * + * @var \Symfony\Component\Routing\RouteCollection + */ protected $routes; - /** @var Symfony\Component\Console\Output\OutputInterface */ + /** + * Console output. + * + * @var Symfony\Component\Console\Output\OutputInterface + */ protected $consoleOutput; - public function __construct() + /** + * Initialize the class. + * + * @param string $host + * @param int $port + * @return void + */ + public function __construct(string $host, int $port) { + $this->host = $host; + $this->port = $port; + $this->loop = LoopFactory::create(); } + /** + * Add the routes. + * + * @param \Symfony\Component\Routing\RouteCollection $routes + * @return $this + */ public function useRoutes(RouteCollection $routes) { $this->routes = $routes; @@ -43,20 +79,12 @@ public function useRoutes(RouteCollection $routes) return $this; } - public function setHost(string $host) - { - $this->host = $host; - - return $this; - } - - public function setPort(string $port) - { - $this->port = $port; - - return $this; - } - + /** + * Set the loop instance. + * + * @param \React\EventLoop\LoopInterface $loop + * @return $this + */ public function setLoop(LoopInterface $loop) { $this->loop = $loop; @@ -64,6 +92,12 @@ public function setLoop(LoopInterface $loop) return $this; } + /** + * Set the console output. + * + * @param \Symfony\Component\Console\Output\OutputInterface $consoleOutput + * @return $this + */ public function setConsoleOutput(OutputInterface $consoleOutput) { $this->consoleOutput = $consoleOutput; @@ -71,6 +105,11 @@ public function setConsoleOutput(OutputInterface $consoleOutput) return $this; } + /** + * Set up the server. + * + * @return \Ratchet\Server\IoServer + */ public function createServer(): IoServer { $socket = new Server("{$this->host}:{$this->port}", $this->loop); diff --git a/src/Statistics/DnsResolver.php b/src/Statistics/DnsResolver.php index ceca9982f3..57cfdcb201 100644 --- a/src/Statistics/DnsResolver.php +++ b/src/Statistics/DnsResolver.php @@ -7,33 +7,53 @@ class DnsResolver implements ResolverInterface { - private $internalIP = '127.0.0.1'; - - /* - * This empty constructor is needed so we don't have to setup the parent's dependencies. + /** + * The internal IP to use. + * + * @var string */ - public function __construct() - { - // - } + private $internalIp = '127.0.0.1'; + /** + * Resolve the DNSes. + * + * @param string $domain + * @return \React\Promise\PromiseInterface + */ public function resolve($domain) { return $this->resolveInternal($domain); } + /** + * Resolve all domains. + * + * @param string $domain + * @param string $type + * @return FulfilledPromise + */ public function resolveAll($domain, $type) { return $this->resolveInternal($domain, $type); } + /** + * Resolve the internal domain. + * + * @param string $domain + * @param string $type + * @return FulfilledPromise + */ private function resolveInternal($domain, $type = null) { - return new FulfilledPromise($this->internalIP); + return new FulfilledPromise($this->internalIp); } + /** + * {@inheritdoc} + */ public function __toString() { - return $this->internalIP; + return $this->internalIp; } } diff --git a/src/Statistics/Events/StatisticsUpdated.php b/src/Statistics/Events/StatisticsUpdated.php index e486180a85..2345f96aec 100644 --- a/src/Statistics/Events/StatisticsUpdated.php +++ b/src/Statistics/Events/StatisticsUpdated.php @@ -13,14 +13,29 @@ class StatisticsUpdated implements ShouldBroadcast { use SerializesModels; - /** @var \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry */ + /** + * The statistic instance that got updated + * + * @var \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry + */ protected $webSocketsStatisticsEntry; + /** + * Initialize the event. + * + * @param \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry $webSocketsStatisticsEntry + * @return void + */ public function __construct(WebSocketsStatisticsEntry $webSocketsStatisticsEntry) { $this->webSocketsStatisticsEntry = $webSocketsStatisticsEntry; } + /** + * Format the broadcasting message. + * + * @return array + */ public function broadcastWith() { return [ @@ -32,13 +47,25 @@ public function broadcastWith() ]; } + /** + * Specify the channel to broadcast on. + * + * @return \Illuminate\Broadcasting\Channel + */ public function broadcastOn() { $channelName = Str::after(DashboardLogger::LOG_CHANNEL_PREFIX.'statistics', 'private-'); - return new PrivateChannel($channelName); + return new PrivateChannel( + Str::after(DashboardLogger::LOG_CHANNEL_PREFIX.'statistics', 'private-') + ); } + /** + * Define the broadcasted event name. + * + * @return string + */ public function broadcastAs() { return 'statistics-updated'; diff --git a/src/Statistics/Http/Controllers/WebSocketStatisticsEntriesController.php b/src/Statistics/Http/Controllers/WebSocketStatisticsEntriesController.php index 8fd758c27b..312230a32d 100644 --- a/src/Statistics/Http/Controllers/WebSocketStatisticsEntriesController.php +++ b/src/Statistics/Http/Controllers/WebSocketStatisticsEntriesController.php @@ -8,6 +8,12 @@ class WebSocketStatisticsEntriesController { + /** + * Store the entry. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ public function store(Request $request) { $validatedAttributes = $request->validate([ diff --git a/src/Statistics/Http/Middleware/Authorize.php b/src/Statistics/Http/Middleware/Authorize.php index 277d8e401b..cadd0d6958 100644 --- a/src/Statistics/Http/Middleware/Authorize.php +++ b/src/Statistics/Http/Middleware/Authorize.php @@ -6,8 +6,17 @@ class Authorize { + /** + * Authorize the request by app secret. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return \Illuminate\Http\Response + */ public function handle($request, $next) { - return is_null(App::findBySecret($request->secret)) ? abort(403) : $next($request); + return is_null(App::findBySecret($request->secret)) + ? abort(403) + : $next($request); } } diff --git a/src/Statistics/Logger/HttpStatisticsLogger.php b/src/Statistics/Logger/HttpStatisticsLogger.php index 1cc0201293..a97c48010d 100644 --- a/src/Statistics/Logger/HttpStatisticsLogger.php +++ b/src/Statistics/Logger/HttpStatisticsLogger.php @@ -12,59 +12,93 @@ class HttpStatisticsLogger implements StatisticsLogger { - /** @var \BeyondCode\LaravelWebSockets\Statistics\Statistic[] */ + /** + * The list of stored statistics. + * + * @var array + */ protected $statistics = []; - /** @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager */ + /** + * The Channel manager. + * + * @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager + */ protected $channelManager; - /** @var \Clue\React\Buzz\Browser */ + /** + * The Browser instance. + * + * @var \Clue\React\Buzz\Browser + */ protected $browser; + /** + * Initialize the logger. + * + * @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager + * @param \Clue\React\Buzz\Browser $browser + * @return void + */ public function __construct(ChannelManager $channelManager, Browser $browser) { $this->channelManager = $channelManager; - $this->browser = $browser; } + /** + * Handle the incoming websocket message. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function webSocketMessage(ConnectionInterface $connection) { - $this - ->findOrMakeStatisticForAppId($connection->app->id) + $this->findOrMakeStatisticForAppId($connection->app->id) ->webSocketMessage(); } + /** + * Handle the incoming API message. + * + * @param mixed $appId + * @return void + */ public function apiMessage($appId) { - $this - ->findOrMakeStatisticForAppId($appId) + $this->findOrMakeStatisticForAppId($appId) ->apiMessage(); } + /** + * Handle the new conection. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function connection(ConnectionInterface $connection) { - $this - ->findOrMakeStatisticForAppId($connection->app->id) + $this->findOrMakeStatisticForAppId($connection->app->id) ->connection(); } + /** + * Handle disconnections. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function disconnection(ConnectionInterface $connection) { - $this - ->findOrMakeStatisticForAppId($connection->app->id) + $this->findOrMakeStatisticForAppId($connection->app->id) ->disconnection(); } - protected function findOrMakeStatisticForAppId($appId): Statistic - { - if (! isset($this->statistics[$appId])) { - $this->statistics[$appId] = new Statistic($appId); - } - - return $this->statistics[$appId]; - } - + /** + * Save all the stored statistics. + * + * @return void + */ public function save() { foreach ($this->statistics as $appId => $statistic) { @@ -76,8 +110,7 @@ public function save() 'secret' => App::findById($appId)->secret, ]); - $this - ->browser + $this->browser ->post( action([WebSocketStatisticsEntriesController::class, 'store']), ['Content-Type' => 'application/json'], @@ -85,7 +118,23 @@ public function save() ); $currentConnectionCount = $this->channelManager->getConnectionCount($appId); + $statistic->reset($currentConnectionCount); } } + + /** + * Find or create a defined statistic for an app. + * + * @param mixed $appId + * @return Statistic + */ + protected function findOrMakeStatisticForAppId($appId): Statistic + { + if (! isset($this->statistics[$appId])) { + $this->statistics[$appId] = new Statistic($appId); + } + + return $this->statistics[$appId]; + } } diff --git a/src/Statistics/Logger/NullStatisticsLogger.php b/src/Statistics/Logger/NullStatisticsLogger.php index 885703e92b..ee8728ef98 100644 --- a/src/Statistics/Logger/NullStatisticsLogger.php +++ b/src/Statistics/Logger/NullStatisticsLogger.php @@ -8,38 +8,82 @@ class NullStatisticsLogger implements StatisticsLogger { - /** @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager */ + /** + * The Channel manager. + * + * @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager + */ protected $channelManager; - /** @var \Clue\React\Buzz\Browser */ + /** + * The Browser instance. + * + * @var \Clue\React\Buzz\Browser + */ protected $browser; + /** + * Initialize the logger. + * + * @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager + * @param \Clue\React\Buzz\Browser $browser + * @return void + */ public function __construct(ChannelManager $channelManager, Browser $browser) { $this->channelManager = $channelManager; $this->browser = $browser; } + /** + * Handle the incoming websocket message. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function webSocketMessage(ConnectionInterface $connection) { // } + /** + * Handle the incoming API message. + * + * @param mixed $appId + * @return void + */ public function apiMessage($appId) { // } + /** + * Handle the new conection. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function connection(ConnectionInterface $connection) { // } + /** + * Handle disconnections. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function disconnection(ConnectionInterface $connection) { // } + /** + * Save all the stored statistics. + * + * @return void + */ public function save() { // diff --git a/src/Statistics/Logger/StatisticsLogger.php b/src/Statistics/Logger/StatisticsLogger.php index 402a58a23c..dcf1d192de 100644 --- a/src/Statistics/Logger/StatisticsLogger.php +++ b/src/Statistics/Logger/StatisticsLogger.php @@ -6,13 +6,42 @@ interface StatisticsLogger { + /** + * Handle the incoming websocket message. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function webSocketMessage(connectionInterface $connection); + /** + * Handle the incoming API message. + * + * @param mixed $appId + * @return void + */ public function apiMessage($appId); + /** + * Handle the new conection. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function connection(connectionInterface $connection); + /** + * Handle disconnections. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function disconnection(connectionInterface $connection); + /** + * Save all the stored statistics. + * + * @return void + */ public function save(); } diff --git a/src/Statistics/Models/WebSocketsStatisticsEntry.php b/src/Statistics/Models/WebSocketsStatisticsEntry.php index 24f0a7f87c..edd0de14ff 100644 --- a/src/Statistics/Models/WebSocketsStatisticsEntry.php +++ b/src/Statistics/Models/WebSocketsStatisticsEntry.php @@ -6,7 +6,13 @@ class WebSocketsStatisticsEntry extends Model { + /** + * {@inheritdoc} + */ protected $guarded = []; + /** + * {@inheritdoc} + */ protected $table = 'websockets_statistics_entries'; } diff --git a/src/Statistics/Rules/AppId.php b/src/Statistics/Rules/AppId.php index d52199ec94..1642d5c0be 100644 --- a/src/Statistics/Rules/AppId.php +++ b/src/Statistics/Rules/AppId.php @@ -7,6 +7,13 @@ class AppId implements Rule { + /** + * Create a new rule. + * + * @param mixed $attribute + * @param mixed $value + * @return bool + */ public function passes($attribute, $value) { $manager = app(AppManager::class); @@ -14,6 +21,11 @@ public function passes($attribute, $value) return $manager->findById($value) ? true : false; } + /** + * The validation message. + * + * @return string + */ public function message() { return 'There is no app registered with the given id. Make sure the websockets config file contains an app for this id or that your custom AppManager returns an app for this id.'; diff --git a/src/Statistics/Statistic.php b/src/Statistics/Statistic.php index 93765fb384..64ee2474e4 100644 --- a/src/Statistics/Statistic.php +++ b/src/Statistics/Statistic.php @@ -6,31 +6,67 @@ class Statistic { - /** @var int|string */ + /** + * The app id. + * + * @var mixed + */ protected $appId; - /** @var int */ + /** + * The current connections count ticker. + * + * @var int + */ protected $currentConnectionCount = 0; - /** @var int */ + /** + * The peak connections count ticker. + * + * @var int + */ protected $peakConnectionCount = 0; - /** @var int */ + /** + * The websockets connections count ticker. + * + * @var int + */ protected $webSocketMessageCount = 0; - /** @var int */ + /** + * The api messages connections count ticker. + * + * @var int + */ protected $apiMessageCount = 0; + /** + * Create a new statistic. + * + * @param mixed $appId + * @return void + */ public function __construct($appId) { $this->appId = $appId; } + /** + * Check if the app has statistics enabled. + * + * @return bool + */ public function isEnabled(): bool { return App::findById($this->appId)->statisticsEnabled; } + /** + * Handle a new connection increment. + * + * @return void + */ public function connection() { $this->currentConnectionCount++; @@ -38,6 +74,11 @@ public function connection() $this->peakConnectionCount = max($this->currentConnectionCount, $this->peakConnectionCount); } + /** + * Handle a disconnection decrement. + * + * @return void + */ public function disconnection() { $this->currentConnectionCount--; @@ -45,16 +86,32 @@ public function disconnection() $this->peakConnectionCount = max($this->currentConnectionCount, $this->peakConnectionCount); } + /** + * Handle a new websocket message. + * + * @return void + */ public function webSocketMessage() { $this->webSocketMessageCount++; } + /** + * Handle a new api message. + * + * @return void + */ public function apiMessage() { $this->apiMessageCount++; } + /** + * Reset all the connections to a specific count. + * + * @param int $currentConnectionCount + * @return void + */ public function reset(int $currentConnectionCount) { $this->currentConnectionCount = $currentConnectionCount; @@ -63,6 +120,11 @@ public function reset(int $currentConnectionCount) $this->apiMessageCount = 0; } + /** + * Transform the statistic to array. + * + * @return array + */ public function toArray() { return [ diff --git a/src/WebSockets/Channels/Channel.php b/src/WebSockets/Channels/Channel.php index cd7e473aed..a08ef36d00 100644 --- a/src/WebSockets/Channels/Channel.php +++ b/src/WebSockets/Channels/Channel.php @@ -11,37 +11,75 @@ class Channel { - /** @var string */ + /** + * The channel name. + * + * @var string + */ protected $channelName; - /** @var ReplicationInterface */ + /** + * The replicator client. + * + * @var ReplicationInterface + */ protected $replicator; - /** @var \Ratchet\ConnectionInterface[] */ + /** + * The connections that got subscribed. + * + * @var array + */ protected $subscribedConnections = []; + /** + * Create a new instance. + * + * @param string $channelName + * @return void + */ public function __construct(string $channelName) { $this->channelName = $channelName; $this->replicator = app(ReplicationInterface::class); } + /** + * Get the channel name. + * + * @return string + */ public function getChannelName(): string { return $this->channelName; } + /** + * Check if the channel has connections. + * + * @return bool + */ public function hasConnections(): bool { return count($this->subscribedConnections) > 0; } + /** + * Get all subscribed connections. + * + * @return array + */ public function getSubscribedConnections(): array { return $this->subscribedConnections; } /** + * Check if the signature for the payload is valid. + * + * @param \Ratchet\ConnectionInterface $connection + * @param \stdClass $payload + * @return void * @throws InvalidSignature */ protected function verifySignature(ConnectionInterface $connection, stdClass $payload) @@ -61,7 +99,12 @@ protected function verifySignature(ConnectionInterface $connection, stdClass $pa } /** - * @link https://pusher.com/docs/pusher_protocol#presence-channel-events + * Subscribe to the channel. + * + * @see https://pusher.com/docs/pusher_protocol#presence-channel-events + * @param \Ratchet\ConnectionInterface $connection + * @param \stdClass $payload + * @return void */ public function subscribe(ConnectionInterface $connection, stdClass $payload) { @@ -75,6 +118,12 @@ public function subscribe(ConnectionInterface $connection, stdClass $payload) $this->replicator->subscribe($connection->app->id, $this->channelName); } + /** + * Unsubscribe connection from the channel. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function unsubscribe(ConnectionInterface $connection) { unset($this->subscribedConnections[$connection->socketId]); @@ -89,6 +138,12 @@ public function unsubscribe(ConnectionInterface $connection) } } + /** + * Store the connection to the subscribers list. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ protected function saveConnection(ConnectionInterface $connection) { $hadConnectionsPreviously = $this->hasConnections(); @@ -107,6 +162,12 @@ protected function saveConnection(ConnectionInterface $connection) ]); } + /** + * Broadcast a payload to the subscribed connections. + * + * @param \stdClass $payload + * @return void + */ public function broadcast($payload) { foreach ($this->subscribedConnections as $connection) { @@ -114,12 +175,30 @@ public function broadcast($payload) } } + /** + * Broadcast the payload, but exclude the current connection. + * + * @param \Ratchet\ConnectionInterface $connection + * @param \stdClass $payload + * @return void + */ public function broadcastToOthers(ConnectionInterface $connection, $payload) { - $this->broadcastToEveryoneExcept($payload, $connection->socketId, $connection->app->id); + $this->broadcastToEveryoneExcept( + $payload, $connection->socketId, $connection->app->id + ); } - public function broadcastToEveryoneExcept($payload, ?string $socketId, string $appId, bool $publish = true) + /** + * Broadcast the payload, but exclude a specific socket id. + * + * @param \stdClass $payload + * @param string|null $socketId + * @param mixed $appId + * @param bool $publish + * @return void + */ + public function broadcastToEveryoneExcept($payload, ?string $socketId, $appId, bool $publish = true) { // Also broadcast via the other websocket server instances. // This is set false in the Redis client because we don't want to cause a loop @@ -145,7 +224,13 @@ public function broadcastToEveryoneExcept($payload, ?string $socketId, string $a } } - public function toArray(string $appId = null) + /** + * Convert the channel to array. + * + * @param mixed $appId + * @return array + */ + public function toArray($appId = null) { return [ 'occupied' => count($this->subscribedConnections) > 0, diff --git a/src/WebSockets/Channels/ChannelManager.php b/src/WebSockets/Channels/ChannelManager.php index cacff7efac..fb1721ac48 100644 --- a/src/WebSockets/Channels/ChannelManager.php +++ b/src/WebSockets/Channels/ChannelManager.php @@ -6,13 +6,45 @@ interface ChannelManager { - public function findOrCreate(string $appId, string $channelName): Channel; + /** + * Find a channel by name or create one. + * + * @param mixed $appId + * @param string $channelName + * @return \BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel + */ + public function findOrCreate($appId, string $channelName): Channel; - public function find(string $appId, string $channelName): ?Channel; + /** + * Find a channel by name. + * + * @param mixed $appId + * @param string $channelName + * @return \BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel + */ + public function find($appId, string $channelName): ?Channel; - public function getChannels(string $appId): array; + /** + * Get all channels. + * + * @param mixed $appId + * @return array + */ + public function getChannels($appId): array; - public function getConnectionCount(string $appId): int; + /** + * Get the connections count on the app. + * + * @param mixed $appId + * @return int + */ + public function getConnectionCount($appId): int; + /** + * Remove connection from all channels. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function removeFromAllChannels(ConnectionInterface $connection); } diff --git a/src/WebSockets/Channels/ChannelManagers/ArrayChannelManager.php b/src/WebSockets/Channels/ChannelManagers/ArrayChannelManager.php index f9c6c20c42..8635a463bc 100644 --- a/src/WebSockets/Channels/ChannelManagers/ArrayChannelManager.php +++ b/src/WebSockets/Channels/ChannelManagers/ArrayChannelManager.php @@ -12,13 +12,28 @@ class ArrayChannelManager implements ChannelManager { - /** @var string */ + /** + * The app id. + * + * @var mixed + */ protected $appId; - /** @var Channel[][] */ + /** + * The list of channels. + * + * @var array + */ protected $channels = []; - public function findOrCreate(string $appId, string $channelName): Channel + /** + * Find a channel by name or create one. + * + * @param mixed $appId + * @param string $channelName + * @return \BeyondCode\LaravelWebSockets\WebSockets\Channels + */ + public function findOrCreate($appId, string $channelName): Channel { if (! isset($this->channels[$appId][$channelName])) { $channelClass = $this->determineChannelClass($channelName); @@ -29,30 +44,36 @@ public function findOrCreate(string $appId, string $channelName): Channel return $this->channels[$appId][$channelName]; } - public function find(string $appId, string $channelName): ?Channel + /** + * Find a channel by name. + * + * @param mixed $appId + * @param string $channelName + * @return \BeyondCode\LaravelWebSockets\WebSockets\Channels + */ + public function find($appId, string $channelName): ?Channel { return $this->channels[$appId][$channelName] ?? null; } - protected function determineChannelClass(string $channelName): string - { - if (Str::startsWith($channelName, 'private-')) { - return PrivateChannel::class; - } - - if (Str::startsWith($channelName, 'presence-')) { - return PresenceChannel::class; - } - - return Channel::class; - } - - public function getChannels(string $appId): array + /** + * Get all channels. + * + * @param mixed $appId + * @return array + */ + public function getChannels($appId): array { return $this->channels[$appId] ?? []; } - public function getConnectionCount(string $appId): int + /** + * Get the connections count on the app. + * + * @param mixed $appId + * @return int + */ + public function getConnectionCount($appId): int { return collect($this->getChannels($appId)) ->flatMap(function (Channel $channel) { @@ -62,28 +83,48 @@ public function getConnectionCount(string $appId): int ->count(); } + /** + * Remove connection from all channels. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function removeFromAllChannels(ConnectionInterface $connection) { if (! isset($connection->app)) { return; } - /* - * Remove the connection from all channels. - */ - collect(Arr::get($this->channels, $connection->app->id, []))->each->unsubscribe($connection); + collect(Arr::get($this->channels, $connection->app->id, [])) + ->each->unsubscribe($connection); - /* - * Unset all channels that have no connections so we don't leak memory. - */ collect(Arr::get($this->channels, $connection->app->id, [])) ->reject->hasConnections() - ->each(function (Channel $channel, string $channelName) use ($connection) { - unset($this->channels[$connection->app->id][$channelName]); - }); + ->each(function (Channel $channel, string $channelName) use ($connection) { + unset($this->channels[$connection->app->id][$channelName]); + }); if (count(Arr::get($this->channels, $connection->app->id, [])) === 0) { unset($this->channels[$connection->app->id]); } } + + /** + * Get the channel class by the channel name. + * + * @param string $channelName + * @return string + */ + protected function determineChannelClass(string $channelName): string + { + if (Str::startsWith($channelName, 'private-')) { + return PrivateChannel::class; + } + + if (Str::startsWith($channelName, 'presence-')) { + return PresenceChannel::class; + } + + return Channel::class; + } } diff --git a/src/WebSockets/Channels/PresenceChannel.php b/src/WebSockets/Channels/PresenceChannel.php index 3217566de9..a3e58aab37 100644 --- a/src/WebSockets/Channels/PresenceChannel.php +++ b/src/WebSockets/Channels/PresenceChannel.php @@ -27,7 +27,7 @@ class PresenceChannel extends Channel * @param string $appId * @return PromiseInterface */ - public function getUsers(string $appId) + public function getUsers($appId) { return $this->replicator->channelMembers($appId, $this->channelName); } @@ -116,7 +116,7 @@ public function unsubscribe(ConnectionInterface $connection) * @param string|null $appId * @return PromiseInterface */ - public function toArray(string $appId = null) + public function toArray($appId = null) { return $this->replicator ->channelMembers($appId, $this->channelName) diff --git a/src/WebSockets/Channels/PrivateChannel.php b/src/WebSockets/Channels/PrivateChannel.php index d68b90b652..5f84308871 100644 --- a/src/WebSockets/Channels/PrivateChannel.php +++ b/src/WebSockets/Channels/PrivateChannel.php @@ -9,6 +9,12 @@ class PrivateChannel extends Channel { /** + * Subscribe to the channel. + * + * @see https://pusher.com/docs/pusher_protocol#presence-channel-events + * @param \Ratchet\ConnectionInterface $connection + * @param \stdClass $payload + * @return void * @throws InvalidSignature */ public function subscribe(ConnectionInterface $connection, stdClass $payload) diff --git a/src/WebSockets/Exceptions/ConnectionsOverCapacity.php b/src/WebSockets/Exceptions/ConnectionsOverCapacity.php index 9b0522f5a5..a685d448d5 100644 --- a/src/WebSockets/Exceptions/ConnectionsOverCapacity.php +++ b/src/WebSockets/Exceptions/ConnectionsOverCapacity.php @@ -4,13 +4,15 @@ class ConnectionsOverCapacity extends WebSocketException { + /** + * Initialize the instance. + * + * @see https://pusher.com/docs/pusher_protocol#error-codes + * @return void + */ public function __construct() { $this->message = 'Over capacity'; - - // @See https://pusher.com/docs/pusher_protocol#error-codes - // Indicates an error resulting in the connection - // being closed by Pusher, and that the client may reconnect after 1s or more. $this->code = 4100; } } diff --git a/src/WebSockets/Exceptions/InvalidConnection.php b/src/WebSockets/Exceptions/InvalidConnection.php index 9a80077bc6..268b55fb33 100644 --- a/src/WebSockets/Exceptions/InvalidConnection.php +++ b/src/WebSockets/Exceptions/InvalidConnection.php @@ -4,10 +4,15 @@ class InvalidConnection extends WebSocketException { + /** + * Initialize the instance. + * + * @see https://pusher.com/docs/pusher_protocol#error-codes + * @return void + */ public function __construct() { $this->message = 'Invalid Connection'; - $this->code = 4009; } } diff --git a/src/WebSockets/Exceptions/InvalidSignature.php b/src/WebSockets/Exceptions/InvalidSignature.php index 71f87a17f4..b0229b3671 100644 --- a/src/WebSockets/Exceptions/InvalidSignature.php +++ b/src/WebSockets/Exceptions/InvalidSignature.php @@ -4,10 +4,15 @@ class InvalidSignature extends WebSocketException { + /** + * Initialize the instance. + * + * @see https://pusher.com/docs/pusher_protocol#error-codes + * @return void + */ public function __construct() { $this->message = 'Invalid Signature'; - $this->code = 4009; } } diff --git a/src/WebSockets/Exceptions/OriginNotAllowed.php b/src/WebSockets/Exceptions/OriginNotAllowed.php index aebbe37af7..87fef2c9b0 100644 --- a/src/WebSockets/Exceptions/OriginNotAllowed.php +++ b/src/WebSockets/Exceptions/OriginNotAllowed.php @@ -4,6 +4,12 @@ class OriginNotAllowed extends WebSocketException { + /** + * Initialize the instance. + * + * @see https://pusher.com/docs/pusher_protocol#error-codes + * @return void + */ public function __construct(string $appKey) { $this->message = "The origin is not allowed for `{$appKey}`."; diff --git a/src/WebSockets/Exceptions/UnknownAppKey.php b/src/WebSockets/Exceptions/UnknownAppKey.php index 6fe5c83765..f872f330a1 100644 --- a/src/WebSockets/Exceptions/UnknownAppKey.php +++ b/src/WebSockets/Exceptions/UnknownAppKey.php @@ -4,7 +4,7 @@ class UnknownAppKey extends WebSocketException { - public function __construct(string $appKey) + public function __construct($appKey) { $this->message = "Could not find app key `{$appKey}`."; diff --git a/src/WebSockets/Exceptions/WebSocketException.php b/src/WebSockets/Exceptions/WebSocketException.php index 5c83cca21e..d38da70fff 100644 --- a/src/WebSockets/Exceptions/WebSocketException.php +++ b/src/WebSockets/Exceptions/WebSocketException.php @@ -6,6 +6,11 @@ class WebSocketException extends Exception { + /** + * Get the payload, Pusher-like formatted. + * + * @return array + */ public function getPayload() { return [ diff --git a/src/WebSockets/Messages/PusherChannelProtocolMessage.php b/src/WebSockets/Messages/PusherChannelProtocolMessage.php index 5217faa2c0..5de2604ec4 100644 --- a/src/WebSockets/Messages/PusherChannelProtocolMessage.php +++ b/src/WebSockets/Messages/PusherChannelProtocolMessage.php @@ -9,15 +9,34 @@ class PusherChannelProtocolMessage implements PusherMessage { - /** @var \stdClass */ + /** + * The payload to send. + * + * @var \stdClass + */ protected $payload; - /** @var \React\Socket\ConnectionInterface */ + /** + * The socket connection. + * + * @var \Ratchet\ConnectionInterface + */ protected $connection; - /** @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager */ + /** + * The channel manager. + * + * @var ChannelManager + */ protected $channelManager; + /** + * Create a new instance. + * + * @param \stdClass $payload + * @param \Ratchet\ConnectionInterface $connection + * @param ChannelManager $channelManager + */ public function __construct(stdClass $payload, ConnectionInterface $connection, ChannelManager $channelManager) { $this->payload = $payload; @@ -27,6 +46,11 @@ public function __construct(stdClass $payload, ConnectionInterface $connection, $this->channelManager = $channelManager; } + /** + * Respond with the payload. + * + * @return void + */ public function respond() { $eventName = Str::camel(Str::after($this->payload->event, ':')); @@ -36,8 +60,12 @@ public function respond() } } - /* - * @link https://pusher.com/docs/pusher_protocol#ping-pong + /** + * Ping the connection. + * + * @see https://pusher.com/docs/pusher_protocol#ping-pong + * @param \Ratchet\ConnectionInterface $connection + * @return void */ protected function ping(ConnectionInterface $connection) { @@ -46,8 +74,13 @@ protected function ping(ConnectionInterface $connection) ])); } - /* - * @link https://pusher.com/docs/pusher_protocol#pusher-subscribe + /** + * Subscribe to channel. + * + * @see https://pusher.com/docs/pusher_protocol#pusher-subscribe + * @param \Ratchet\ConnectionInterface $connection + * @param \stdClass $payload + * @return void */ protected function subscribe(ConnectionInterface $connection, stdClass $payload) { @@ -56,6 +89,13 @@ protected function subscribe(ConnectionInterface $connection, stdClass $payload) $channel->subscribe($connection, $payload); } + /** + * Unsubscribe from the channel. + * + * @param \Ratchet\ConnectionInterface $connection + * @param \stdClass $payload + * @return void + */ public function unsubscribe(ConnectionInterface $connection, stdClass $payload) { $channel = $this->channelManager->findOrCreate($connection->app->id, $payload->channel); diff --git a/src/WebSockets/Messages/PusherClientMessage.php b/src/WebSockets/Messages/PusherClientMessage.php index 1ef519cdc1..cab08d15d5 100644 --- a/src/WebSockets/Messages/PusherClientMessage.php +++ b/src/WebSockets/Messages/PusherClientMessage.php @@ -10,24 +10,46 @@ class PusherClientMessage implements PusherMessage { - /** \stdClass */ + /** + * The payload to send. + * + * @var \stdClass + */ protected $payload; - /** @var \Ratchet\ConnectionInterface */ + /** + * The socket connection. + * + * @var \Ratchet\ConnectionInterface + */ protected $connection; - /** @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager */ + /** + * The channel manager. + * + * @var ChannelManager + */ protected $channelManager; + /** + * Create a new instance. + * + * @param \stdClass $payload + * @param \Ratchet\ConnectionInterface $connection + * @param ChannelManager $channelManager + */ public function __construct(stdClass $payload, ConnectionInterface $connection, ChannelManager $channelManager) { $this->payload = $payload; - $this->connection = $connection; - $this->channelManager = $channelManager; } + /** + * Respond to the message construction. + * + * @return void + */ public function respond() { if (! Str::startsWith($this->payload->event, 'client-')) { diff --git a/src/WebSockets/Messages/PusherMessage.php b/src/WebSockets/Messages/PusherMessage.php index bed95507b4..4a7e23e4f0 100644 --- a/src/WebSockets/Messages/PusherMessage.php +++ b/src/WebSockets/Messages/PusherMessage.php @@ -4,5 +4,10 @@ interface PusherMessage { + /** + * Respond to the message construction. + * + * @return void + */ public function respond(); } diff --git a/src/WebSockets/Messages/PusherMessageFactory.php b/src/WebSockets/Messages/PusherMessageFactory.php index 7fbe512da4..0136449992 100644 --- a/src/WebSockets/Messages/PusherMessageFactory.php +++ b/src/WebSockets/Messages/PusherMessageFactory.php @@ -9,6 +9,14 @@ class PusherMessageFactory { + /** + * Create a new message. + * + * @param \Ratchet\RFC6455\Messaging\MessageInterface $message + * @param \Ratchet\ConnectionInterface $connection + * @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager + * @return PusherMessage + */ public static function createForMessage( MessageInterface $message, ConnectionInterface $connection, diff --git a/src/WebSockets/WebSocketHandler.php b/src/WebSockets/WebSocketHandler.php index 3a49a4de90..7a2537ec85 100644 --- a/src/WebSockets/WebSocketHandler.php +++ b/src/WebSockets/WebSocketHandler.php @@ -19,24 +19,46 @@ class WebSocketHandler implements MessageComponentInterface { - /** @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager */ + /** + * The channel manager. + * + * @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager + */ protected $channelManager; + /** + * Initialize a new handler. + * + * @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager + * @return void + */ public function __construct(ChannelManager $channelManager) { $this->channelManager = $channelManager; } + /** + * Handle the socket opening. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function onOpen(ConnectionInterface $connection) { - $this - ->verifyAppKey($connection) + $this->verifyAppKey($connection) ->verifyOrigin($connection) ->limitConcurrentConnections($connection) ->generateSocketId($connection) ->establishConnection($connection); } + /** + * Handle the incoming message. + * + * @param \Ratchet\ConnectionInterface $connection + * @param \Ratchet\RFC6455\Messaging\MessageInterface $message + * @return void + */ public function onMessage(ConnectionInterface $connection, MessageInterface $message) { $message = PusherMessageFactory::createForMessage($message, $connection, $this->channelManager); @@ -46,6 +68,12 @@ public function onMessage(ConnectionInterface $connection, MessageInterface $mes StatisticsLogger::webSocketMessage($connection); } + /** + * Handle the websocket close. + * + * @param \Ratchet\ConnectionInterface $connection + * @return void + */ public function onClose(ConnectionInterface $connection) { $this->channelManager->removeFromAllChannels($connection); @@ -57,6 +85,13 @@ public function onClose(ConnectionInterface $connection) StatisticsLogger::disconnection($connection); } + /** + * Handle the websocket errors. + * + * @param \Ratchet\ConnectionInterface $connection + * @param WebSocketException $exception + * @return void + */ public function onError(ConnectionInterface $connection, Exception $exception) { if ($exception instanceof WebSocketException) { @@ -66,6 +101,12 @@ public function onError(ConnectionInterface $connection, Exception $exception) } } + /** + * Verify the app key validity. + * + * @param \Ratchet\ConnectionInterface $connection + * @return $this + */ protected function verifyAppKey(ConnectionInterface $connection) { $appKey = QueryParameters::create($connection->httpRequest)->get('appKey'); @@ -79,6 +120,12 @@ protected function verifyAppKey(ConnectionInterface $connection) return $this; } + /** + * Verify the origin. + * + * @param \Ratchet\ConnectionInterface $connection + * @return $this + */ protected function verifyOrigin(ConnectionInterface $connection) { if (! $connection->app->allowedOrigins) { @@ -96,6 +143,12 @@ protected function verifyOrigin(ConnectionInterface $connection) return $this; } + /** + * Limit the connections count by the app. + * + * @param \Ratchet\ConnectionInterface $connection + * @return $this + */ protected function limitConcurrentConnections(ConnectionInterface $connection) { if (! is_null($capacity = $connection->app->capacity)) { @@ -108,6 +161,12 @@ protected function limitConcurrentConnections(ConnectionInterface $connection) return $this; } + /** + * Create a socket id. + * + * @param \Ratchet\ConnectionInterface $connection + * @return $this + */ protected function generateSocketId(ConnectionInterface $connection) { $socketId = sprintf('%d.%d', random_int(1, 1000000000), random_int(1, 1000000000)); @@ -117,6 +176,12 @@ protected function generateSocketId(ConnectionInterface $connection) return $this; } + /** + * Establish connection with the client. + * + * @param \Ratchet\ConnectionInterface $connection + * @return $this + */ protected function establishConnection(ConnectionInterface $connection) { $connection->send(json_encode([ diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index 672468ea71..8431724479 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -23,6 +23,11 @@ class WebSocketsServiceProvider extends ServiceProvider { + /** + * Boot the service provider. + * + * @return void + */ public function boot() { $this->publishes([ @@ -33,8 +38,7 @@ public function boot() __DIR__.'/../database/migrations/0000_00_00_000000_create_websockets_statistics_entries_table.php' => database_path('migrations/0000_00_00_000000_create_websockets_statistics_entries_table.php'), ], 'migrations'); - $this - ->registerRoutes() + $this->registerDashboardRoutes() ->registerDashboardGate(); $this->loadViewsFrom(__DIR__.'/../resources/views/', 'websockets'); @@ -48,6 +52,35 @@ public function boot() $this->configurePubSub(); } + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + $this->mergeConfigFrom(__DIR__.'/../config/websockets.php', 'websockets'); + + $this->app->singleton('websockets.router', function () { + return new Router(); + }); + + $this->app->singleton(ChannelManager::class, function () { + $channelManager = config('websockets.managers.channel', ArrayChannelManager::class); + + return new $channelManager; + }); + + $this->app->singleton(AppManager::class, function () { + return $this->app->make(config('websockets.managers.app')); + }); + } + + /** + * Configure the PubSub replication. + * + * @return void + */ protected function configurePubSub() { $this->app->make(BroadcastManager::class)->extend('websockets', function ($app, array $config) { @@ -69,26 +102,12 @@ protected function configurePubSub() }); } - public function register() - { - $this->mergeConfigFrom(__DIR__.'/../config/websockets.php', 'websockets'); - - $this->app->singleton('websockets.router', function () { - return new Router(); - }); - - $this->app->singleton(ChannelManager::class, function () { - $channelManager = config('websockets.managers.channel', ArrayChannelManager::class); - - return new $channelManager; - }); - - $this->app->singleton(AppManager::class, function () { - return $this->app->make(config('websockets.managers.app')); - }); - } - - protected function registerRoutes() + /** + * Register the dashboard routes. + * + * @return void + */ + protected function registerDashboardRoutes() { Route::prefix(config('websockets.dashboard.path'))->group(function () { Route::middleware(config('websockets.dashboard.middleware', [AuthorizeDashboard::class]))->group(function () { @@ -106,6 +125,11 @@ protected function registerRoutes() return $this; } + /** + * Register the dashboard gate. + * + * @return void + */ protected function registerDashboardGate() { Gate::define('viewWebSocketsDashboard', function ($user = null) { diff --git a/tests/ClientProviders/AppTest.php b/tests/ClientProviders/AppTest.php index 683a805c92..b20e38f1b0 100644 --- a/tests/ClientProviders/AppTest.php +++ b/tests/ClientProviders/AppTest.php @@ -13,7 +13,7 @@ public function it_can_create_a_client() { new App(1, 'appKey', 'appSecret'); - $this->markTestAsPassed(); + $this->assertTrue(true); } /** @test */ diff --git a/tests/HttpApi/FetchUsersReplicationTest.php b/tests/HttpApi/FetchUsersReplicationTest.php index 39d79c34ae..9fa7a9615d 100644 --- a/tests/HttpApi/FetchUsersReplicationTest.php +++ b/tests/HttpApi/FetchUsersReplicationTest.php @@ -22,7 +22,7 @@ public function setUp(): void } /** @test */ - public function test_invalid_signatures_can_not_access_the_api() + public function invalid_signatures_can_not_access_the_api() { $this->expectException(HttpException::class); $this->expectExceptionMessage('Invalid auth signature provided.'); @@ -45,7 +45,7 @@ public function test_invalid_signatures_can_not_access_the_api() } /** @test */ - public function test_it_only_returns_data_for_presence_channels() + public function it_only_returns_data_for_presence_channels() { $this->expectException(HttpException::class); $this->expectExceptionMessage('Invalid presence channel'); @@ -70,7 +70,7 @@ public function test_it_only_returns_data_for_presence_channels() } /** @test */ - public function test_it_returns_404_for_invalid_channels() + public function it_returns_404_for_invalid_channels() { $this->expectException(HttpException::class); $this->expectExceptionMessage('Unknown channel'); @@ -95,7 +95,7 @@ public function test_it_returns_404_for_invalid_channels() } /** @test */ - public function test_it_returns_connected_user_information() + public function it_returns_connected_user_information() { $this->skipOnRedisReplication(); diff --git a/tests/TestCase.php b/tests/TestCase.php index 9deb436a3e..85f390222a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -18,10 +18,18 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase { - /** @var \BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler */ + /** + * A test Pusher server. + * + * @var \BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler + */ protected $pusherServer; - /** @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager */ + /** + * The test Channel manager. + * + * @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager + */ protected $channelManager; /** @@ -120,18 +128,33 @@ protected function getEnvironmentSetUp($app) } } + /** + * Get the websocket connection for a specific URL. + * + * @param mixed $appKey + * @param array $headers + * @return \BeyondCode\LaravelWebSockets\Tests\Mocks\Connection + */ protected function getWebSocketConnection(string $appKey = 'TestKey', array $headers = []): Connection { - $connection = new Connection(); + $connection = new Connection; $connection->httpRequest = new Request('GET', "/?appKey={$appKey}", $headers); return $connection; } + /** + * Get a connected websocket connection. + * + * @param array $channelsToJoin + * @param string $appKey + * @param array $headers + * @return \BeyondCode\LaravelWebSockets\Tests\Mocks\Connection + */ protected function getConnectedWebSocketConnection(array $channelsToJoin = [], string $appKey = 'TestKey', array $headers = []): Connection { - $connection = new Connection(); + $connection = new Connection; $connection->httpRequest = new Request('GET', "/?appKey={$appKey}", $headers); @@ -151,6 +174,12 @@ protected function getConnectedWebSocketConnection(array $channelsToJoin = [], s return $connection; } + /** + * Join a presence channel. + * + * @param string $channel + * @return \BeyondCode\LaravelWebSockets\Tests\Mocks\Connection + */ protected function joinPresenceChannel($channel): Connection { $connection = $this->getWebSocketConnection(); @@ -180,11 +209,23 @@ protected function joinPresenceChannel($channel): Connection return $connection; } + /** + * Get a channel from connection. + * + * @param \Ratchet\ConnectionInterface $connection + * @param string $channelName + * @return \BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel|null + */ protected function getChannel(ConnectionInterface $connection, string $channelName) { return $this->channelManager->findOrCreate($connection->app->id, $channelName); } + /** + * Configure the replicator clients. + * + * @return void + */ protected function configurePubSub() { // Replace the publish and subscribe clients with a Mocked @@ -204,11 +245,6 @@ protected function configurePubSub() } } - protected function markTestAsPassed() - { - $this->assertTrue(true); - } - protected function runOnlyOnRedisReplication() { if (config('websockets.replication.driver') !== 'redis') { @@ -237,6 +273,11 @@ protected function skipOnLocalReplication() } } + /** + * Get the subscribed client for the replication. + * + * @return ReplicationInterface + */ protected function getSubscribeClient() { return $this->app @@ -244,6 +285,11 @@ protected function getSubscribeClient() ->getSubscribeClient(); } + /** + * Get the publish client for the replication. + * + * @return ReplicationInterface + */ protected function getPublishClient() { return $this->app From f83a66900027882d6ce5c356c4aae08362f52270 Mon Sep 17 00:00:00 2001 From: rennokki Date: Tue, 18 Aug 2020 20:21:44 +0300 Subject: [PATCH 2/2] Apply fixes from StyleCI (#470) --- src/Apps/AppManager.php | 2 +- src/Apps/ConfigAppManager.php | 2 -- src/Server/Logger/ConnectionLogger.php | 2 +- src/Server/Logger/HttpLogger.php | 2 +- src/Server/Logger/WebsocketsLogger.php | 2 +- src/Statistics/Events/StatisticsUpdated.php | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Apps/AppManager.php b/src/Apps/AppManager.php index ef8cb860be..03c0c9ee0a 100644 --- a/src/Apps/AppManager.php +++ b/src/Apps/AppManager.php @@ -4,7 +4,7 @@ interface AppManager { - /** + /** * Get all apps. * * @return array[\BeyondCode\LaravelWebSockets\Apps\App] diff --git a/src/Apps/ConfigAppManager.php b/src/Apps/ConfigAppManager.php index c029d71b2b..3136ad65c5 100644 --- a/src/Apps/ConfigAppManager.php +++ b/src/Apps/ConfigAppManager.php @@ -2,8 +2,6 @@ namespace BeyondCode\LaravelWebSockets\Apps; -use Illuminate\Support\Collection; - class ConfigAppManager implements AppManager { /** diff --git a/src/Server/Logger/ConnectionLogger.php b/src/Server/Logger/ConnectionLogger.php index e87c78c217..4a1b02d721 100644 --- a/src/Server/Logger/ConnectionLogger.php +++ b/src/Server/Logger/ConnectionLogger.php @@ -17,7 +17,7 @@ class ConnectionLogger extends Logger implements ConnectionInterface * Create a new instance and add a connection to watch. * * @param \Ratchet\ConnectionInterface $connection - * @return Self + * @return self */ public static function decorate(ConnectionInterface $app): self { diff --git a/src/Server/Logger/HttpLogger.php b/src/Server/Logger/HttpLogger.php index 4ff6e12032..6b5f1726a0 100644 --- a/src/Server/Logger/HttpLogger.php +++ b/src/Server/Logger/HttpLogger.php @@ -19,7 +19,7 @@ class HttpLogger extends Logger implements MessageComponentInterface * Create a new instance and add the app to watch. * * @param \Ratchet\MessageComponentInterface $app - * @return Self + * @return self */ public static function decorate(MessageComponentInterface $app): self { diff --git a/src/Server/Logger/WebsocketsLogger.php b/src/Server/Logger/WebsocketsLogger.php index 7d600b148b..bc206c8417 100644 --- a/src/Server/Logger/WebsocketsLogger.php +++ b/src/Server/Logger/WebsocketsLogger.php @@ -21,7 +21,7 @@ class WebsocketsLogger extends Logger implements MessageComponentInterface * Create a new instance and add the app to watch. * * @param \Ratchet\MessageComponentInterface $app - * @return Self + * @return self */ public static function decorate(MessageComponentInterface $app): self { diff --git a/src/Statistics/Events/StatisticsUpdated.php b/src/Statistics/Events/StatisticsUpdated.php index 2345f96aec..4f82bb7ddc 100644 --- a/src/Statistics/Events/StatisticsUpdated.php +++ b/src/Statistics/Events/StatisticsUpdated.php @@ -14,7 +14,7 @@ class StatisticsUpdated implements ShouldBroadcast use SerializesModels; /** - * The statistic instance that got updated + * The statistic instance that got updated. * * @var \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry */