WebSocket

This is a very important feature of this package. Generally in Laravel when you want to build a WebSocket server, you have to rely on third-party WebSocket server. The most common way is to use Pusher or Socket.io provided by NodeJS and with the combination of Broadcasting. There's no way to build a WebSocket server in Laravel.

Now with Swoole, it is possible and not difficult to integrate with Laravel. Nowadays when we talk about WebSocket, the majority choose Socket.io to be their solution. The good news is this package is compatible with Socket.io protocol (partially). That means in the browser or other clients you can use Socket.io client directly.

These Socket.io features are not implemented:

  • namespace
  • path
  • long polling
  • sid (sid interacts with servers will be ignored)
  • only supports text type data

WebSocket Event Binding

After you run vendor:publish, you can find websocket.php in your routes folder. This is where you can register your WebSocket events. It looks pretty much like how you register an HTTP route in Laravel. You can register events with on function, and it supports callback function or handler path.

  1. <?php
  2.  
  3. use SwooleTW\Http\Websocket\Facades\Websocket;
  4.  
  5. /*
  6. |--------------------------------------------------------------------------
  7. | Websocket Routes
  8. |--------------------------------------------------------------------------
  9. |
  10. | Here is where you can register Websocket events for your application.
  11. |
  12. */
  13.  
  14. Websocket::on('connect', function ($websocket, $request) {
  15. // in connect callback, illuminate request will be injected here
  16. $websocket->emit('message', 'welcome');
  17. });
  18.  
  19. Websocket::on('disconnect', function ($websocket) {
  20. // this callback will be triggered when a websocket is disconnected
  21. });
  22.  
  23. Websocket::on('example', function ($websocket, $data) {
  24. $websocket->emit('message', $data);
  25. });
  26.  
  27. Websocket::on('test', 'ExampleController@method');

In callback functions, you can get $websocket and $data as parameters.

Parameter sequence doesn't matter, but parameter names does.

Middleware for onConnect Event Request

connect is a special event which will be triggered after the WebSocket connection is established. You can do some authorization here according to the request. Here you can use middleware for pre-processing the request. The default middleware are set in config/swoole_websocket.php.

  1. 'middleware' => [
  2. SwooleTW\Http\Websocket\Middleware\DecryptCookies::class,
  3. SwooleTW\Http\Websocket\Middleware\StartSession::class,
  4. SwooleTW\Http\Websocket\Middleware\Authenticate::class,
  5. ],

There are some built-in middleware for fetching auth user with session guard. You can customize your middleware for your authorization.

Middleware will be executed by sequence. Getting a session auth user needs to decrypt cookies first, then start a session, and finally get auth user.

  1. Websocket::on('connect', function ($websocket, $request) {
  2. $request->user();
  3. auth()->user();
  4. });

Getting authorized user is as easy as what you usually do in Laravel.

  1. Websocket::on('connect', function ($websocket, $request) {
  2. //
  3. })->middleware(FooBarMiddleware::class);

You can also append your middleware after the on function.

In onConnect event, you can't make any HTTP response back to the client here. Here's just a callback function after the connection established.

Authenticated User Binding

After getting the authenticated user, you can bind the user to established socket connection. You can choose to emit events to the specific user, and all bound sockets will receive the message.

  1. // bind a user to current socket, user must implement AuthenticatableContract class
  2. Websocket::loginUsing($user);
  3.  
  4. // bind a user using user id
  5. Websocket::loginUsingId($userId);
  6.  
  7. // get current auth user id by sender's fd
  8. Websocket::getUserId();
  9.  
  10. // emit message to specific user
  11. Websocket::toUser($user)->emit('message', 'hi there');
  12.  
  13. // emit message to multiple users
  14. Websocket::toUser([$userA, $userB])->emit('message', 'hi there');
  15.  
  16. // emit message to specific user id
  17. Websocket::toUserId($userId)->emit('message', 'hi there');
  18.  
  19. // emit message to multiple user ids
  20. Websocket::toUserId([$userId1, $userId2])->emit('message', 'hi there');

WebSocket APIs

FunctionParametersDescription
onstring $event, callable $callbackListen to a event name, and bind to handler
broadcastNoneSet current message to broadcast
toint string array $valuesAssigned to specific or multiple fds or rooms
loginUsingAuthenticatable $userBind user to current socket
loginUsingIdstring $uidBind user id to current socket
logoutNoneLogout by current sender's fd
getUserIdNoneGet current auth user id by sender's socket
toUserAuthenticatable $user or arraySet multiple recipients' fds by users
toUserIdstring $userId or arraySet multiple recipients' fds by userIds
isUserIdOnlinestring $userIdCheck if there's any online sockets with this user id
joinstring array $roomsJoin current sender to one or multiple rooms
leavestring array $roomMake current sender leave one or multiple rooms
emitstring $event, mixed $dataEmit an event with data
closeNoneClose current connection
getSenderNoneGet current sender's fd
getIsBroadcastNoneGet if is broadcast now
getToNoneGet assigned fds and rooms
resetboolean $forceReset isBroadcast, to and sender (if force is true)

Usage Examples

The usage of Websocket refers to Socket.io, so they look highly similar and friendly for those developers who have experience in Socket.io.

  1. <?php
  2.  
  3. use SwooleTW\Http\Websocket\Facades\Websocket;
  4.  
  5. // sending to sender-client only
  6. Websocket::emit('message', 'this is a test');
  7.  
  8. // sending to all clients except sender
  9. Websocket::broadcast()->emit('message', 'this is a test');
  10.  
  11. // sending to all clients in 'game' room except sender
  12. Websocket::broadcast()->to('game')->emit('message', 'nice game');
  13.  
  14. // sending to all clients in 'game1' and 'game2' rooms except sender
  15. Websocket::broadcast()->to('game1')->to('game2')->emit('message', 'nice game');
  16. Websocket::broadcast()->to(['game1', 'game2'])->emit('message', 'nice game');
  17.  
  18. // sending to all clients in 'game' including sender client
  19. Websocket::to('game')->emit('message', 'enjoy the game');
  20.  
  21. // sending to individual socketid 1 (can't be sender)
  22. Websocket::broadcast()->to(1)->emit('message', 'for your eyes only');
  23.  
  24. // sending to socketid 1 and 2 (can't be sender)
  25. Websocket::broadcast()->to(1)->to(2)->emit('message', 'for your eyes only');
  26. Websocket::broadcast()->to([1, 2])->emit('message', 'for your eyes only');
  27.  
  28. // join to subscribe the socket to a given channel
  29. Websocket::join('some room');
  30.  
  31. // leave to unsubscribe the socket to a given channel
  32. Websocket::leave('some room');
  1. <?php
  2.  
  3. use SwooleTW\Http\Websocket\Facades\Room;
  4.  
  5. // get all fds in a 'game' room
  6. Room::getClients('game');
  7.  
  8. // get all rooms of fd 1
  9. Room::getRooms(1);
  10.  
  11. // add fd 1 to a 'game' room
  12. Room::add(1, 'room');
  13.  
  14. // add fd 1 to 'game' and 'test' rooms
  15. Room::add(1, ['game', 'test']);
  16.  
  17. // delete fd 1 from a 'game' room
  18. Room::delete(1, 'room');
  19.  
  20. // delete fd 1 from 'game' and 'test' rooms
  21. Room::delete(1, ['game', 'test']);

Customization

Default handler uses Socket.io, but you can still use your customized WebSocket protocol.

There are two classes you need to deal with:

  • Websocket Handler: Need to implement HandlerContract
  1. <?php
  2.  
  3. namespace SwooleTW\Http\Websocket;
  4.  
  5. use Illuminate\Http\Request;
  6. use Swoole\Websocket\Frame;
  7.  
  8. interface HandlerContract
  9. {
  10. /**
  11. * "onOpen" listener.
  12. *
  13. * @param int $fd
  14. * @param \Illuminate\Http\Request $request
  15. */
  16. public function onOpen($fd, Request $request);
  17.  
  18. /**
  19. * "onMessage" listener.
  20. * only triggered when event handler not found
  21. *
  22. * @param \Swoole\Websocket\Frame $frame
  23. */
  24. public function onMessage(Frame $frame);
  25.  
  26. /**
  27. * "onClose" listener.
  28. *
  29. * @param int $fd
  30. * @param int $reactorId
  31. */
  32. public function onClose($fd, $reactorId);
  33. }
  • Packet Parser: Need to extend Parser
  1. <?php
  2.  
  3. namespace SwooleTW\Http\Websocket\Foo;
  4.  
  5. use Swoole\Websocket\Frame;
  6. use SwooleTW\Http\Websocket\Parser;
  7.  
  8. class FooParser extends Parser
  9. {
  10. /**
  11. * Strategy classes need to implement handle method.
  12. */
  13. protected $strategies = [
  14. // optional
  15. ];
  16.  
  17. /**
  18. * Encode output message for websocket push.
  19. *
  20. * @return mixed
  21. */
  22. public function encode($event, $data)
  23. {
  24. // your formatiing logic
  25. return $result;
  26. }
  27.  
  28. /**
  29. * Decode message from websocket client.
  30. * Define and return payload here.
  31. *
  32. * @param \Swoole\Websocket\Frame $frame
  33. * @return array
  34. */
  35. public function decode(Frame $frame)
  36. {
  37. // your formatiing logic
  38. return [
  39. 'event' => $yourParsedEventName,
  40. 'data' => $yourParsedData
  41. ];
  42. }
  43. }

And configure them with your class path in config/swoole_websocket.php

  1. 'handler' => SwooleTW\Http\Websocket\SocketIO\WebsocketHandler::class,
  2. 'parser' => SwooleTW\Http\Websocket\SocketIO\SocketIOParser::class,