sfcod / socketio
SocketIo adapter for Symfony
Installs: 2 458
Dependents: 0
Suggesters: 0
Security: 0
Stars: 25
Watchers: 4
Forks: 7
Open Issues: 1
Type:symfony-bundle
Requires
- php: ^7.4
- ezyang/htmlpurifier: ^4.0
- predis/predis: ^1.1
- symfony/dotenv: ^5.4
- symfony/framework-bundle: ^5.4
- symfony/monolog-bundle: ^3.1
- symfony/process: ^5.4
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.8
- symfony/phpunit-bridge: ^5.4
README
Use all power of socket.io in your Symfony project.
Config
Install node, after install npm
npm install --prefix ./vendor/sfcod/socketio/Server
services: App\SocketIo\: resource: '../src/SocketIo/*' tags: ['sfcod.socketio.event'] # If you want override default JoinHandler/LeaveHandler. For example, add some condition for join/leave a room. exclude: - '../src/SocketIo/JoinHandler.php' # Optional - '../src/SocketIo/LeaveHandler.php' # Optional # If you want override default JoinHandler SfCod\SocketIoBundle\Service\JoinHandler: class: App\SocketIo\JoinHandler tags: ['sfcod.socketio.event'] # If you want override default LeaveHandler SfCod\SocketIoBundle\Service\LeaveHandler: class: App\SocketIo\LeaveHandler tags: ['sfcod.socketio.event']
Extend base class or use SF decorator. Ovveride fire or handle methods if it needs.
namespace App\SocketIo; use SfCod\SocketIoBundle\Service\JoinHandler as JoinHandlerBase; class JoinHandler extends JoinHandlerBase { public function fire(): array { // Some additional logic here. // ... return array_merge_recursive(parent::fire(), [ 'key' => 'value' ]); } }
Process Middlewares
If you use doctrine, then you can connect "doctrine reconnect", then it will be reconnect for each process.
sfcod_socketio: processMiddlewares: - 'SfCod\SocketIoBundle\Middleware\Process\DoctrineReconnect'
###> socketio config ### SOCKET_IO_WS_SERVER=localhost:1358 SOCKET_IO_WS_CLIENT=localhost:1358 SOCKET_IO_SSL='' || '{"key":"path to key", "cert":"path to cert"}' SOCKET_IO_NSP=redis ###< socketio config ###
JWT token auth. Put SOCKET_IO_AUTH_TOKEN_PATH OR SOCKET_IO_AUTH_TOKEN_VALUE
#Public jwt token key path (Will be join with base path) SOCKET_IO_AUTH_TOKEN_PATH='/config/jwt/public.pem' #Public key value SOCKET_IO_AUTH_TOKEN_VALUE='public key value' #You can change token name. Default name is 'token' SOCKET_IO_AUTH_TOKEN_NAME='token'
var socket = io('{your_host_address}:1367/notifications', { query: { token: 'yourTokenHere', }, });
Usage
Start nodejs server
php bin/console socket-io:node-js-server
Start php server
php bin/console socket-io:php-server
Create publisher from server to client
use SfCod\SocketIoBundle\Events\EventInterface; use SfCod\SocketIoBundle\Events\EventPublisherInterface; use SfCod\SocketIoBundle\Events\AbstractEvent; class CountEvent extends AbstractEvent implements EventInterface, EventPublisherInterface { /** * Changel name. For client side this is nsp. */ public static function broadcastOn(): array { return ['notifications']; } /** * Event name */ public static function name(): string { return 'update_notification_count'; } /** * Emit client event * @return array */ public function fire(): array { return [ 'count' => 10, ]; } }
var socket = io('{your_host_address}:1367/notifications'); socket.on('update_notification_count', function(data){ console.log(data) });
Create receiver from client to server
use SfCod\SocketIoBundle\Events\EventInterface; use SfCod\SocketIoBundle\Events\EventSubscriberInterface; use SfCod\SocketIoBundle\Events\AbstractEvent; use SfCod\SocketIoBundle\Service\Broadcast; class MarkAsReadEvent extends AbstractEvent implements EventInterface, EventSubscriberInterface { private $broadcast; public function __construct(Broadcast $broadcast) { $this->broadcast = $broadcast; } /** * Changel name. For client side this is nsp. */ public static function broadcastOn(): array { return ['notifications']; } /** * Event name */ public static function name(): string { return 'mark_as_read_notification'; } /** * Handle client event */ public function handle() { // Mark notification as read // And call client update $this->broadcast->emit('update_notification_count', ['some key' => 'some value']); } }
var socket = io('{your_host_address}:1367/notifications'); socket.emit('mark_as_read_notification', {id: 10});
You can have publisher and receiver in one event. If you need check data from client to server you should use:
- EventPolicyInterface
Receiver with checking from client to server
use SfCod\SocketIoBundle\Events\EventSubscriberInterface; use SfCod\SocketIoBundle\Events\EventInterface; use SfCod\SocketIoBundle\Events\EventPolicyInterface; use SfCod\SocketIoBundle\Events\AbstractEvent; use SfCod\SocketIoBundle\Service\Broadcast; class MarkAsReadEvent extends AbstractEvent implements EventInterface, EventSubscriberInterface, EventPolicyInterface { private $broadcast; public function __construct(Broadcast $broadcast) { $this->broadcast = $broadcast; } /** * Changel name. For client side this is nsp. */ public static function broadcastOn(): array { return ['notifications']; } /** * Event name */ public static function name(): string { return 'mark_as_read_notification'; } public function can($data): bool { // Check data from client return true; } /** * Emit client event * @return array */ public function handle() { // Mark notification as read // And call client update $this->broadcast->emit('update_notification_count', ['some key' => 'some value']); } }
Socket.io rooms
- EventRoomInterface (Backend side)
use SfCod\SocketIoBundle\Events\EventPublisherInterface; use SfCod\SocketIoBundle\Events\EventInterface; use SfCod\SocketIoBundle\Events\EventRoomInterface; use SfCod\SocketIoBundle\Events\AbstractEvent; class CountEvent extends AbstractEvent implements EventInterface, EventPublisherInterface, EventRoomInterface { /** * Changel name. For client side this is nsp. */ public static function broadcastOn(): array { return ['notifications']; } /** * Event name */ public static function name(): string { return 'update_notification_count'; } /** * Socket.io room * @return string */ public function room(): string { return 'user_id_' . $this->userId; } /** * Emit client event * @return array */ public function fire(): array { return [ 'count' => 10, ]; } }
Client side
var socket = io('{your_host_address}:1367/notifications'); socket.emit('join', {room: 'user_id_10'}); // Now you will receive data from 'room-1' socket.on('update_notification_count', function(data){ console.log(data) }); // You can leave room socket.emit('leave', {room: 'user_id_10'});
Run this on the backend side
$this->broadcast->emit('update_notification_count', ['some key' => 'some value', 'userId' => 10]);
Default events:
- connection
- disconnect
- join
- leave
use SfCod\SocketIoBundle\Events\EventSubscriberInterface; use SfCod\SocketIoBundle\Events\EventInterface; use Psr\Log\LoggerInterface; use SfCod\SocketIoBundle\Events\AbstractEvent; class СonnectionEvent extends AbstractEvent implements EventInterface, EventSubscriberInterface { private $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } /** * Changel name. For client side this is nsp. */ public static function broadcastOn(): array { return ['notifications']; } /** * Event name */ public static function name(): string { return 'connection'; // or 'disconnect' } /** * Handle client event */ public function handle() { // Socket.io ID // $this->socketId $this->logger->info('disconnect', $this->payload); } }