Skip to content

System Room Controller

The SystemRoomController is a built-in controller that automatically handles room management for WebSocket connections.

Overview

By default, Sockeon registers a system controller that processes join_room and leave_room events from clients, making room management seamless out-of-the-box.

Features

  • ✅ Automatic room join/leave handling
  • ✅ Namespace support
  • ✅ Input validation (room names, namespaces)
  • ✅ Confirmation events (room_joined, room_left)
  • ✅ Error handling with detailed error events
  • ✅ Can be disabled or overridden

Events Handled

join_room

Client sends this event to join a room.

Expected data:

json
{
  "room": "room-name",
  "namespace": "/namespace" // optional, defaults to "/"
}

Success response:

json
{
  "event": "room_joined",
  "data": {
    "room": "room-name",
    "namespace": "/namespace",
    "timestamp": 1234567890
  }
}

Error response:

json
{
  "event": "error",
  "data": {
    "message": "Invalid room name",
    "event": "join_room"
  }
}

leave_room

Client sends this event to leave a room.

Expected data:

json
{
  "room": "room-name",
  "namespace": "/namespace" // optional, defaults to "/"
}

Success response:

json
{
  "event": "room_left",
  "data": {
    "room": "room-name",
    "namespace": "/namespace",
    "timestamp": 1234567890
  }
}

Configuration

Enable/Disable System Controllers

By default, system controllers are enabled. You can disable them:

php
use Sockeon\Sockeon\Config\ServerConfig;
use Sockeon\Sockeon\Connection\Server;

$config = new ServerConfig([
    'host' => '0.0.0.0',
    'port' => 6001,
    'register_system_controllers' => false, // Disable system controllers
]);

$server = new Server($config);

Custom Room Logic

Option 1: Disable and Implement Your Own

php
$config = new ServerConfig([
    'register_system_controllers' => false,
]);

$server = new Server($config);

// Register your custom controller
$server->registerController(new MyCustomRoomController());

MyCustomRoomController.php:

php
use Sockeon\Sockeon\Controllers\SocketController;
use Sockeon\Sockeon\WebSocket\Attributes\SocketOn;

class MyCustomRoomController extends SocketController
{
    #[SocketOn('join_room')]
    public function handleJoinRoom(string $clientId, array $data): bool
    {
        // Add your custom logic here
        // - Authentication checks
        // - Room capacity limits
        // - Permission validation
        // - Logging
        
        $room = $data['room'] ?? null;
        $namespace = $data['namespace'] ?? '/';
        
        // Check if user has permission to join
        if (!$this->hasPermission($clientId, $room)) {
            $this->emit($clientId, 'error', [
                'message' => 'You do not have permission to join this room',
            ]);
            return false;
        }
        
        // Join the room
        $this->joinRoom($clientId, $room, $namespace);
        
        // Send custom confirmation
        $this->emit($clientId, 'room_joined', [
            'room' => $room,
            'namespace' => $namespace,
            'members' => $this->getClientsInRoom($room, $namespace),
        ]);
        
        return true;
    }
    
    private function hasPermission(string $clientId, string $room): bool
    {
        // Your permission logic
        return true;
    }
}

Option 2: Extend SystemRoomController

php
use Sockeon\Sockeon\Controllers\SystemRoomController;

class EnhancedRoomController extends SystemRoomController
{
    #[SocketOn('join_room')]
    public function onJoinRoom(string $clientId, array $data): bool
    {
        // Add pre-processing logic
        $this->logRoomJoin($clientId, $data);
        
        // Call parent implementation
        $result = parent::onJoinRoom($clientId, $data);
        
        // Add post-processing logic
        if ($result) {
            $this->notifyRoomMembers($clientId, $data['room']);
        }
        
        return $result;
    }
    
    #[SocketOn('leave_room')]
    public function onLeaveRoom(string $clientId, array $data): bool
    {
        $result = parent::onLeaveRoom($clientId, $data);
        
        if ($result) {
            $this->notifyRoomMembers($clientId, $data['room']);
        }
        
        return $result;
    }
    
    private function logRoomJoin(string $clientId, array $data): void
    {
        $this->getLogger()->info("Client $clientId joining room: {$data['room']}");
    }
    
    private function notifyRoomMembers(string $clientId, string $room): void
    {
        $this->broadcast('room.member_update', [
            'clientId' => $clientId,
            'room' => $room,
            'action' => 'joined',
        ], '/', $room);
    }
}

Then register your custom controller:

php
$config = new ServerConfig([
    'register_system_controllers' => false, // Disable default
]);

$server = new Server($config);
$server->registerController(new EnhancedRoomController());

Client Usage

Works seamlessly with @sockeon/client:

javascript
import { Sockeon } from '@sockeon/client';

const socket = new Sockeon({
  url: 'ws://localhost:6001',
});

socket.on('connect', () => {
  // Join a room
  socket.joinRoom('game-room-42');
});

// Listen for confirmation
socket.on('room_joined', (data) => {
  console.log('Joined room:', data.room);
});

// Listen for errors
socket.on('error', (error) => {
  console.error('Error:', error.message);
});

// Leave room
socket.leaveRoom('game-room-42');

Validation

The system controller validates:

  1. Room name: Must be a non-empty string
  2. Namespace: Must be a non-empty string (defaults to /)

Invalid requests receive an error event.

Error Handling

All errors are caught and:

  • Logged via the server logger
  • Sent to the client as an error event

This ensures the connection remains stable even if room operations fail.

Thread Safety

The SystemRoomController is thread-safe when used with Sockeon's single-threaded event loop.

Best Practices

  1. Always listen for confirmation events (room_joined, room_left) in your client
  2. Handle error events to provide user feedback
  3. Use namespaces to isolate different parts of your application
  4. Override with custom logic when you need authentication or permissions
  5. Log important room operations for debugging and analytics