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/sockeonBasic 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.