Getting Started with Sockeon

This comprehensive guide will help you set up Sockeon in your PHP project and create a dual-protocol server that handles both WebSockets and HTTP requests. Learn how to build real-time applications with WebSockets while simultaneously serving HTTP content and RESTful APIs from the same codebase.

System Requirements

  • PHP 8.1 or higher
  • Composer dependency manager
  • ext-sockets PHP extension enabled

Installation

Install Sockeon using Composer:

composer require sockeon/sockeon

Basic Usage

1. Creating a Server Instance

use Sockeon\Sockeon\Core\Server;

// Initialize server on localhost:8000
$server = new Server(
    host: "0.0.0.0", 
    port: 8000,
    debug: false,
    corsConfig: [
        'allowed_origins' => ['*'],
        'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
        'allowed_headers' => ['Content-Type', 'X-Requested-With', 'Authorization'],
        'allow_credentials' => false,
        'max_age' => 86400
    ],
    logger: new \Sockeon\Sockeon\Logging\Logger(
        minLogLevel: \Sockeon\Sockeon\Logging\LogLevel::INFO,
        logToConsole: true,
        logToFile: true,
        logDirectory: __DIR__ . '/logs'
    )
);

// Start the server
$server->run();

2. Create a Controller

use Sockeon\Sockeon\Core\Contracts\SocketController;
use Sockeon\Sockeon\WebSocket\Attributes\SocketOn;
use Sockeon\Sockeon\Http\Attributes\HttpRoute;
use Sockeon\Sockeon\Http\Request;
use Sockeon\Sockeon\Http\Response;

class ChatController extends SocketController
{
    #[SocketOn('message.send')]
    public function onMessage(int $clientId, array $data)
    {
        // Handle incoming message
        $this->broadcast('message.receive', [
            'from' => $clientId,
            'message' => $data['message']
        ]);
    }

    #[HttpRoute('GET', '/status')]
    public function getStatus(Request $request)
    {
        return Response::json([
            'status' => 'online',
            'time' => date('Y-m-d H:i:s')
        ]);
    }
}

3. Register the Controller

$server->registerController(new ChatController());

4. Add Middleware (Optional)

// Add WebSocket middleware
$server->addWebSocketMiddleware(function ($clientId, $event, $data, $next) {
    echo "WebSocket Event: $event from client $clientId\n";
    return $next();
});

// Add HTTP middleware
$server->addHttpMiddleware(function (Request $request, $next) {
    echo "HTTP Request: {$request->getMethod()} {$request->getPath()}\n";
    return $next();
});

WebSocket Client Example

const socket = new WebSocket('ws://localhost:8000');

socket.onopen = () => {
    console.log('Connected to server');
    
    // Send a message
    socket.send(JSON.stringify({
        event: 'message.send',
        data: {
            message: 'Hello, World!'
        }
    }));
};

socket.onmessage = (event) => {
    const data = JSON.parse(event.data);
    console.log('Received:', data);
};

Cross-Origin Resource Sharing (CORS)

Sockeon provides built-in support for CORS, allowing you to control which origins can connect to your server:

// Configure CORS when creating the server
$server = new Server(
    host: "0.0.0.0",
    port: 8000,
    debug: false,
    corsConfig: [
        'allowed_origins' => ['https://example.com', 'https://app.example.com'],
        'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE'],
        'allowed_headers' => ['Content-Type', 'Authorization'],
        'allow_credentials' => true,
        'max_age' => 86400
    ]
);

For WebSocket connections, the origin header is validated against the allowed origins list. For HTTP requests, appropriate CORS headers are automatically added to responses.

Logging

Sockeon provides a flexible logging system that follows PSR-3 standards:

// Import the necessary classes
use Sockeon\Sockeon\Core\Server;
use Sockeon\Sockeon\Logging\Logger;
use Sockeon\Sockeon\Logging\LogLevel;

// Create a server with custom logger
$server = new Server(
    host: "0.0.0.0",
    port: 8000,
    debug: false,
    corsConfig: [],
    logger: new Logger(
        minLogLevel: LogLevel::INFO,        // Only log INFO and higher
        logToConsole: true,                 // Output to console
        logToFile: true,                    // Write to file
        logDirectory: __DIR__ . '/logs',    // Store logs here
        separateLogFiles: false             // Single log file
    )
);

// Using the logger in your application
$server->getLogger()->info("Server starting on port 8000");
$server->getLogger()->error("Database connection failed", ['host' => 'db.example.com']);

// Log an exception with context data
try {
    // Some code that might throw an exception
} catch (\Exception $e) {
    $server->getLogger()->exception($e, [
        'requestId' => $requestId,
        'user' => $userId
    ]);
}

Environment-Specific Logging

For development environments, enable detailed logging:

$logger = new Logger(
    minLogLevel: LogLevel::DEBUG,    // Capture all log levels
    logToConsole: true,              // Show logs in console for immediate feedback
    logToFile: true,                 // Also write to file
    logDirectory: __DIR__ . '/logs', 
    separateLogFiles: false          // Single combined file for easier review
);

For production environments, configure more focused logging:

$logger = new Logger(
    minLogLevel: LogLevel::WARNING,  // Only capture significant issues
    logToConsole: false,             // Don't output to console for performance
    logToFile: true,                 // Write all logs to file
    logDirectory: '/var/log/sockeon',
    separateLogFiles: true           // Separate files for easier filtering
);

Using Rooms

#[SocketOn('room.join')]
public function onJoinRoom(int $clientId, array $data)
{
    $room = $data['room'] ?? null;
    if ($room) {
        $this->joinRoom($clientId, $room);
        $this->emit($clientId, 'room.joined', [
            'room' => $room
        ]);
    }
}

#[SocketOn('message.room')]
public function onRoomMessage(int $clientId, array $data)
{
    $room = $data['room'] ?? null;
    if ($room) {
        $this->broadcast('message.receive', [
            'from' => $clientId,
            'message' => $data['message']
        ], '/', $room);
    }
}

Special Events

Sockeon provides special attributes to handle connection lifecycle events automatically:

use Sockeon\Sockeon\WebSocket\Attributes\OnConnect;
use Sockeon\Sockeon\WebSocket\Attributes\OnDisconnect;

class ChatController extends SocketController
{
    #[OnConnect]
    public function onClientConnect(int $clientId): void
    {
        // Called automatically when a client connects
        $this->emit($clientId, 'welcome', [
            'message' => 'Welcome to the chat!',
            'clientId' => $clientId
        ]);
        
        $this->server->getLogger()->info("Client connected", [
            'clientId' => $clientId
        ]);
    }

    #[OnDisconnect]  
    public function onClientDisconnect(int $clientId): void
    {
        // Called automatically when a client disconnects
        $this->broadcast('user.left', [
            'clientId' => $clientId,
            'message' => "Client {$clientId} has left"
        ]);
        
        $this->server->getLogger()->info("Client disconnected", [
            'clientId' => $clientId  
        ]);
    }
}

These special events are triggered automatically by the server - you don't need to emit them manually. They're perfect for initialization, cleanup, and notification tasks.