PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
214: WebSockets with Ratchet
Wait, PHP is request-response. How does Ratchet actually keep a connection open?
This is usually the first "aha!" moment when you dive into WebSockets. You're used to the standard PHP lifecycle: a request comes in, PHP scripts run, they spit out HTML, and the process dies. If you tried to do that with WebSockets, you'd be reconnecting every single second, which completely defeats the purpose.
Ratchet works differently because it doesn't run inside Apache or Nginx. It runs as a standalone CLI process. It uses an event loop (via ReactPHP) that basically says, "I'm going to sit here and listen on this specific port. When something happens—like a new connection or a message arriving—I'll trigger a specific function." It's a long-running process, meaning your script stays in memory. I'll warn you now: this means you have to be way more careful about memory leaks than you do in a standard Laravel or Symfony controller.
How do I actually track who is connected and broadcast messages to them?
You can't just use a global array to keep track of users because you need a way to uniquely identify the connection object itself. Ratchet provides a MessageComponentInterface that you implement. Inside this, I always recommend using SplObjectStorage. It's essentially a map that lets you store the connection objects as keys, which is perfect for this.
Let's imagine we're building a Live Order Alert system for a restaurant kitchen. When a new order comes in, we want to blast it to all the kitchen tablets.
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class OrderNotifier implements MessageComponentInterface {
protected $clients;
public function __construct() {
// SplObjectStorage is a lifesaver for tracking connections
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "New tablet connected! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
// We receive an order from the front-of-house system
// and broadcast it to every connected kitchen tablet
foreach ($this->clients as $client) {
$client->send("NEW ORDER: " . $msg);
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e) {
$conn->close();
}
}
Do I still need my web server, or does Ratchet replace it?
You still need your web server. This is a common point of confusion. Ratchet is a WebSocket server, not a Web server. It handles the ws:// or wss:// protocol, not http://.
In a real-world setup, your user's browser will first load your page via Nginx (port 80/443). Then, the JavaScript on that page will initiate a connection to your Ratchet server running on a different port—usually something like 8080. If you're deploying this to production, you'll likely use Nginx as a reverse proxy to route traffic from port 80 to port 8080 so you don't have to open random ports on your firewall.
On the client side, it's surprisingly simple. You don't need a heavy library; the native browser API handles it:
const conn = new WebSocket('ws://localhost:8080');
conn.onopen = function(e) {
console.log("Connected to the kitchen feed!");
};
conn.onmessage = function(e) {
alert("Alert: " + e.data);
};
📋 Practical Task
Build a Real-Time Multi-User Stock Ticker
Your task is to expand the OrderNotifier logic to create a stock price broadcaster. Instead of broadcasting every message to everyone, implement a system where the server only sends messages to clients who have "subscribed" to a specific stock symbol.
- Modify the
onMessagemethod so that it expects a JSON payload like{"action": "subscribe", "symbol": "AAPL"}or{"action": "update", "symbol": "AAPL", "price": "150.00"}. - Use a second
SplObjectStorageor a nested array to keep track of whichConnectionInterfaceobjects are subscribed to which symbols. - Ensure that when an "update" message is received, only the connections subscribed to that specific symbol receive the price update.
- Test it by opening three different browser tabs: subscribe Tab A to "AAPL", Tab B to "TSLA", and Tab C to "AAPL". Send an update for "AAPL" and verify that Tab B receives nothing while A and C do.
There are no comments for now.