PHP Logocboden/Ratchet

cboden/Ratchet is a PHP library that allows developers to build real-time, bi-directional communication applications using WebSockets. Unlike traditional HTTP, which is stateless and requires clients to poll the server for updates, WebSockets provide a persistent, full-duplex communication channel between a client and a server. This enables "push" capabilities from the server to the client, leading to more responsive and efficient real-time applications.\n\nWhy Use Ratchet?\n* Real-time Interaction: Facilitates instant updates for chat applications, live dashboards, online gaming, notifications, and more.\n* Reduced Overhead: Eliminates the need for constant HTTP polling, reducing server load and network traffic.\n* Server Push: Allows the server to initiate communication and send data to clients without them explicitly requesting it.\n* PHP Ecosystem: Brings WebSocket server capabilities directly into the PHP environment, leveraging existing PHP skills and tools.\n\nCore Components of Ratchet:\nRatchet integrates with `ReactPHP` for its event-driven, non-blocking I/O model, which is crucial for handling multiple concurrent WebSocket connections efficiently. Key components include:\n* `Ratchet\\MessageComponentInterface`: This is the heart of your application. You implement this interface to define how your application handles different WebSocket events:\n * `onOpen(ConnectionInterface \$conn)`: Called when a new client connects.\n * `onMessage(ConnectionInterface \$from, \$msg)`: Called when a client sends a message.\n * `onClose(ConnectionInterface \$conn)`: Called when a client disconnects.\n * `onError(ConnectionInterface \$conn, \\Exception \$e)`: Called when an error occurs on a connection.\n* `Ratchet\\WebSocket\\WsServer`: Wraps your `MessageComponentInterface` implementation, handling the WebSocket handshake and framing of messages according to the WebSocket protocol.\n* `Ratchet\\Http\\HttpServer`: Handles standard HTTP requests (e.g., the initial WebSocket handshake).\n* `Ratchet\\Server\\IoServer`: The main server component that binds everything together. It listens for incoming connections on a specific port and passes them to the appropriate handlers (HTTP or WebSocket). It requires an `EventLoop` (provided by `ReactPHP`) to manage asynchronous operations.\n* `React\\EventLoop\\LoopInterface`: The asynchronous event loop that powers Ratchet, allowing it to handle many connections concurrently without blocking.\n\nHow Ratchet Works:\n1. Client Connection: A client (e.g., a web browser using JavaScript's `WebSocket` API) attempts to connect to the Ratchet server via a `ws://` or `wss://` URL.\n2. HTTP Handshake: The initial connection is an HTTP request. `Ratchet\\Http\\HttpServer` handles this.\n3. WebSocket Upgrade: If the HTTP request includes the correct `Upgrade: websocket` header, `Ratchet\\WebSocket\\WsServer` performs the WebSocket handshake, upgrading the connection from HTTP to WebSocket.\n4. Persistent Connection: Once upgraded, a persistent, bi-directional connection is established.\n5. Event Handling: Your application, implementing `MessageComponentInterface`, receives events (`onOpen`, `onMessage`, `onClose`, `onError`) for each connection.\n6. Real-time Communication: Messages can now be sent back and forth between the server and any connected client over this persistent channel.\n\nInstallation (via Composer):\nTo use Ratchet, you typically install it via Composer:\n`composer require cboden/ratchet react/socket react/http`\n\nExample Use Case:\nA simple chat application where multiple clients can connect and send messages to each other in real-time.

Example Code

<?php\nuse Ratchet\\MessageComponentInterface;\nuse Ratchet\\ConnectionInterface;\nuse Ratchet\\Server\\IoServer;\nuse Ratchet\\Http\\HttpServer;\nuse Ratchet\\WebSocket\\WsServer;\nuse React\\EventLoop\\Loop;\n\nrequire __DIR__ . '/vendor/autoload.php';\n\n// Our chat application component\nclass Chat implements MessageComponentInterface {\n    protected \$clients;\n\n    public function __construct() {\n        \$this->clients = new \\SplObjectStorage; // Store all connected clients\n    }\n\n    public function onOpen(ConnectionInterface \$conn) {\n        // Store the new connection to send messages to later\n        \$this->clients->attach(\$conn);\n        echo "New connection! ({\$conn->resourceId})\n";\n    }\n\n    public function onMessage(ConnectionInterface \$from, \$msg) {\n        \$numRecv = count(\$this->clients) - 1;\n        echo sprintf('Connection %d sending message \"%s\" to %d other connection(s)' . "\n"\n            , \$from->resourceId, \$msg, \$numRecv);\n\n        foreach (\$this->clients as \$client) {\n            if (\$from !== \$client) {\n                // The sender is not the receiver, so send to others\n                \$client->send(\$msg);\n            }\n        }\n    }\n\n    public function onClose(ConnectionInterface \$conn) {\n        // Detach the connection when it's closed\n        \$this->clients->detach(\$conn);\n        echo "Connection {\$conn->resourceId} has disconnected\n";\n    }\n\n    public function onError(ConnectionInterface \$conn, \\Exception \$e) {\n        echo "An error has occurred: {\$e->getMessage()}\n";\n        \$conn->close();\n    }\n}\n\n// Set up the Ratchet server\n\$loop = Loop::get(); // Get the default event loop\n\n\$server = IoServer::factory(\n    new HttpServer(\n        new WsServer(\n            new Chat()\n        )\n    ),\n    8080, // Port to listen on\n    '0.0.0.0', // Listen on all available network interfaces\n    \$loop // Pass the event loop\n);\n\necho "Ratchet server started on port 8080\n";\n\$server->run();\n\n/*\nTo run this example:\n1. Make sure you have Composer installed.\n2. Create a directory (e.g., 'ratchet_chat').\n3. Create `composer.json` inside it:\n   ```json\n   {\n       \"require\": {\n           \"cboden/ratchet\": \"^0.4\",\n           \"react/socket\": \"^1.0\",\n           \"react/http\": \"^1.0\"\n       },\n       \"autoload\": {\n           \"psr-4\": {\n               \"App\\\": \"src/\"\n           }\n       }\n   }\n   ```\n4. Run `composer install`.\n5. Save the PHP code above as `server.php` in the root of your project.\n6. Open your terminal and run `php server.php`.\n\nClient-side HTML/JavaScript (save as `index.html` in the same directory and open in browser):\n```html\n<!DOCTYPE html>\n<html>\n<head>\n    <title>Ratchet Chat Client</title>\n    <style>\n        body { font-family: sans-serif; }\n        #messages { border: 1px solid #ccc; padding: 10px; height: 300px; overflow-y: scroll; margin-bottom: 10px; }\n        .message { margin-bottom: 5px; }\n    </style>\n</head>\n<body>\n    <h1>Ratchet Chat Client</h1>\n    <div id=\"messages\"></div>\n    <input type=\"text\" id=\"messageInput\" placeholder=\"Type your message...\" style=\"width: 80%;\">\n    <button id=\"sendButton\">Send</button>\n\n    <script>\n        const conn = new WebSocket('ws://localhost:8080');\n        const messagesDiv = document.getElementById('messages');\n        const messageInput = document.getElementById('messageInput');\n        const sendButton = document.getElementById('sendButton');\n\n        function appendMessage(msg, type = 'received') {\n            const p = document.createElement('p');\n            p.className = 'message ' + type;\n            p.textContent = msg;\n            messagesDiv.appendChild(p);\n            messagesDiv.scrollTop = messagesDiv.scrollHeight; // Scroll to bottom\n        }\n\n        conn.onopen = function(e) {\n            console.log(\"Connection established!\");\n            appendMessage(\"Connected to chat server.\", 'system');\n        };\n\n        conn.onmessage = function(e) {\n            console.log(e.data);\n            appendMessage(\"Other: \" + e.data, 'received');\n        };\n\n        conn.onclose = function(e) {\n            console.log(\"Connection closed.\");\n            appendMessage(\"Disconnected from chat server.\", 'system');\n        };\n\n        conn.onerror = function(e) {\n            console.error(\"WebSocket Error: \", e);\n            appendMessage(\"Connection error: \" + e.message, 'error');\n        };\n\n        sendButton.onclick = function() {\n            const message = messageInput.value;\n            if (message) {\n                conn.send(message);\n                appendMessage(\"You: \" + message, 'sent');\n                messageInput.value = ''; // Clear input\n            }\n        };\n\n        messageInput.addEventListener('keypress', function(e) {\n            if (e.key === 'Enter') {\n                sendButton.click();\n            }\n        });\n    </script>\n</body>\n</html>\n```\n*/