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
243: Message Queues Between PHP Services (RabbitMQ)
A few years ago, I was consulting for a startup that had built a fairly standard e-commerce platform. Everything was fine until they hit a growth spurt. Their checkout process was a single, massive PHP script: it processed the payment, updated the inventory, generated a PDF invoice, and sent a confirmation email—all before sending the final "Thank You" page to the user. One Tuesday, their third-party email provider started lagging. Suddenly, users were staring at a white screen for 30 seconds, getting timed out by Nginx, and—because the page didn't load—they were hitting 'Refresh' and accidentally placing the same order three times. It was a nightmare.
The problem wasn't the PHP code; it was the architecture. We were forcing the user to wait for tasks that didn't actually need to happen in real-time. This is exactly why we use message queues like RabbitMQ. Instead of doing everything right now, the web request simply drops a "note" (a message) into a queue and tells the user, "We've got your order!" and finishes the request in milliseconds. A separate PHP process, running in the background, picks up those notes and does the heavy lifting at its own pace.
Decoupling the Request from the Heavy Lifting
In a typical PHP environment, your script dies the moment the HTML is sent to the browser. You can't just start a background thread and hope for the best. RabbitMQ acts as the middleman—the "Broker"—that lives outside your PHP process. When your web app wants to trigger a slow task, it acts as a Producer. It sends a message to an Exchange, which then routes it into a Queue.
The magic happens when you run a Consumer. This is just another PHP script, but instead of being triggered by a URL, it runs as a long-lived CLI process (usually managed by something like Supervisor). It sits there, listening to the queue, and processes messages as they arrive. If your email provider goes down again, the messages just sit safely in RabbitMQ until the service comes back online. Your users never see a spinning wheel, and your database doesn't lock up.
Wiring Up the Producer and Consumer
To get this working in PHP, you'll likely use the php-amqplib library. I've found that the trickiest part for most developers is understanding that the Producer shouldn't care who is reading the message. It just needs to know which exchange to send it to.
// The Producer (Inside your Controller)
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
// We declare the queue to make sure it exists before we send to it
$channel->queue_declare('order_processing', false, true, false, false);
$data = json_encode(['order_id' => 12345, 'user_email' => 'dev@example.com']);
$msg = new AMQPMessage($data, ['delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]);
$channel->basic_publish($msg, '', 'order_processing');
$channel->close();
$connection->close();
Now, the Consumer is a separate file that you run from the terminal. I always recommend wrapping your consumer logic in a try-catch block; if the consumer crashes and you aren't using a process manager, your queue will just grow indefinitely without anyone processing it.
// The Consumer (Run via CLI: php consumer.php)
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('order_processing', false, true, false, false);
echo " [*] Waiting for orders. To exit press CTRL+C\n";
$callback = function ($msg) {
$order = json_decode($msg->body, true);
echo " [x] Processing Order: ", $order['order_id'], "\n";
// Simulate a slow task like sending an email or generating a PDF
sleep(5);
echo " [x] Done!\n";
// This tells RabbitMQ the message was handled and can be deleted
$msg->ack();
};
$channel->basic_consume('order_processing', '', false, false, false, false, $callback);
while ($channel->is_consuming()) {
$channel->wait();
}
Handling Reliability and Acknowledgments
One detail I can't stress enough: always use manual acknowledgments (the $msg->ack() call). By default, RabbitMQ can be configured to remove a message the second it's sent to a consumer. But what happens if your consumer script hits a fatal error or the server reboots halfway through processing an order? If you've already "acknowledged" the message, that order is gone forever. By using manual ACKs, RabbitMQ will wait for your script to confirm success. If the connection drops before the ACK is sent, RabbitMQ simply puts the message back in the queue for another consumer to try again. It's a simple safety net that saves you from massive data loss.
📋 Practical Task
Build an Asynchronous User Welcome Sequence
Instead of making a new user wait for three different API calls during registration, you are going to build a decoupled welcome system. Your goal is to ensure the registration request finishes instantly, while the "heavy" work happens in the background.
Requirements:
- The Producer: Create a script
register.phpthat simulates a user registration. It should accept a username and email, save them to a mock database (or just an array), and publish a message to a queue nameduser_welcome_queuecontaining the user's ID and email. - The Consumer: Create a script
welcome_worker.phpthat listens touser_welcome_queue. For every message it receives, it must simulate three distinct tasks:- Sending a "Welcome" email (
sleep(2)). - Creating a default user profile in a database (
sleep(1)). - Notifying the admin team via a Slack webhook (
sleep(1)).
- Sending a "Welcome" email (
- Reliability: Ensure the consumer uses manual acknowledgments so that if the script is killed during the "Welcome Email" phase, the task is not lost.
Testing your build: Run welcome_worker.php in one terminal window. Run register.php in another (or via browser) and observe how the registration completes immediately while the worker processes the tasks sequentially in the background.
There are no comments for now.