Back to Articles
45 min read

High-Performance PHP: JIT, Asynchronous Concurrency, and Event-Driven Architecture

PHP is no longer bound by the synchronous request-response cycle. This technical deep dive explores the bleeding edge of the ecosystem: optimizing runtime efficiency with the JIT compiler and FFI, handling thousands of concurrent connections via Swoole and ReactPHP, and decoupling heavy workloads through enterprise-grade message queues and search indexing.

PHP Advanced Topics Guide

Performance Optimization

Profiling (Xdebug, Blackfire, Tideways)

Profiling tools analyze your PHP code's runtime behavior to identify bottlenecks, memory leaks, and slow functions. Xdebug is free and great for development, Blackfire provides production-safe profiling with a beautiful UI, and Tideways offers continuous monitoring with minimal overhead.

┌─────────────────────────────────────────────────────────┐
│ Profiling Tool Comparison │
├──────────────┬─────────────┬─────────────┬─────────────┤
│ Feature │ Xdebug │ Blackfire │ Tideways │
├──────────────┼─────────────┼─────────────┼─────────────┤
│ Production │ ❌ │ ✅ │ ✅ │
│ Overhead │ High │ Low │ Low │
│ Cost │ Free │ Paid │ Paid │
│ Tracing │ ✅ │ ✅ │ ✅ │
└──────────────┴─────────────┴─────────────┴─────────────┘

Benchmarking

Benchmarking measures code execution time and resource usage to compare different implementations objectively. Use microtime(true) for simple benchmarks or tools like PHPBench for comprehensive testing with statistical analysis.

<?php // Simple benchmark $start = microtime(true); $memory_start = memory_get_usage(); // Code to benchmark for ($i = 0; $i < 100000; $i++) { $arr[] = $i; } $time = (microtime(true) - $start) * 1000; $memory = (memory_get_usage() - $memory_start) / 1024; echo "Time: {$time}ms | Memory: {$memory}KB";

Memory Management

PHP allocates memory from the OS in chunks and manages it through the Zend Memory Manager; understanding memory_limit, peak usage tracking, and releasing references (unset()) is crucial for processing large datasets.

<?php ini_set('memory_limit', '256M'); // Processing large file without loading entirely into memory function processLargeFile(string $file): Generator { $handle = fopen($file, 'r'); while (($line = fgets($handle)) !== false) { yield $line; // Yields one line at a time } fclose($handle); } // Memory stays constant regardless of file size foreach (processLargeFile('huge.csv') as $line) { processLine($line); }

Garbage Collection

PHP's garbage collector handles circular references that reference counting alone cannot free; it runs automatically when the root buffer is full (10,000 entries) or can be triggered manually with gc_collect_cycles().

┌─────────────────────────────────────────────────────────┐
│ Circular Reference Problem │
│ │
│ $a ──────────────► Object A │
│ │ ▲ │
│ │ │ │
│ ▼ │ │
│ Object B │
│ │
│ unset($a) → refcount=1, not freed without GC! │
└─────────────────────────────────────────────────────────┘
<?php gc_enable(); // Enable garbage collector gc_collect_cycles(); // Force collection echo gc_status()['runs']; // Check GC statistics

OpCode Optimization

OPcache stores precompiled PHP bytecode in shared memory, eliminating the parse-compile cycle on every request—this alone can improve performance by 2-3x; tune opcache.memory_consumption and opcache.max_accelerated_files for your workload.

┌─────────────────────────────────────────────────────────┐
│ Without OPcache │
│ Request → Parse → Compile → Execute → Response │
│ ▲ │
│ └── Repeated every request! │
├─────────────────────────────────────────────────────────┤
│ With OPcache │
│ Request ──────────────────► Execute → Response │
│ ▲ ▲ │
│ │ ┌───────────────┘ │
│ │ │ Cached bytecode │
│ └────┴───────────────── │
└─────────────────────────────────────────────────────────┘
; php.ini opcache.enable=1 opcache.memory_consumption=256 opcache.max_accelerated_files=20000 opcache.validate_timestamps=0 ; Disable in production

JIT Compilation (PHP 8+)

Just-In-Time compilation in PHP 8+ compiles hot bytecode paths directly to machine code at runtime, providing significant speedups (20-30%) for CPU-intensive code like mathematical computations, though typical web applications see minimal gains due to I/O bottlenecks.

; php.ini - Enable JIT opcache.jit=1255 opcache.jit_buffer_size=100M
┌─────────────────────────────────────────────────────────┐
│ PHP Execution Pipeline │
│ │
│ Source → Tokens → AST → OpCodes → [JIT] → Machine Code │
│ │ │ │
│ ▼ ▼ │
│ Interpreted Native execution │
│ (CPU-bound boost) │
└─────────────────────────────────────────────────────────┘

Database Query Optimization

Optimize queries by adding proper indexes, using EXPLAIN to analyze query plans, avoiding SELECT *, batching writes, and leveraging prepared statements which the database can cache and reuse.

<?php // ❌ Bad: No index usage, fetches everything $users = $pdo->query("SELECT * FROM users WHERE YEAR(created_at) = 2024"); // ✅ Good: Uses index, specific columns, parameterized $stmt = $pdo->prepare(" SELECT id, name, email FROM users WHERE created_at BETWEEN :start AND :end "); $stmt->execute(['start' => '2024-01-01', 'end' => '2024-12-31']);
-- Always check your query plan EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';

N+1 Query Problem

The N+1 problem occurs when you execute 1 query to fetch N records, then N additional queries to fetch related data for each record—solve it by eager loading with JOIN or using WHERE IN to batch the related queries.

<?php // ❌ N+1 Problem: 1 + N queries $posts = $pdo->query("SELECT * FROM posts")->fetchAll(); foreach ($posts as $post) { $author = $pdo->query("SELECT * FROM users WHERE id = {$post['user_id']}")->fetch(); } // ✅ Solution: 2 queries total (or 1 with JOIN) $posts = $pdo->query("SELECT * FROM posts")->fetchAll(); $userIds = implode(',', array_column($posts, 'user_id')); $users = $pdo->query("SELECT * FROM users WHERE id IN ($userIds)")->fetchAll(PDO::FETCH_UNIQUE); foreach ($posts as $post) { $author = $users[$post['user_id']]; }
Queries: 1 (posts) + 100 (authors) = 101 queries ❌
Queries: 1 (posts) + 1 (all authors) = 2 queries ✅

Lazy Loading vs Eager Loading

Lazy loading defers fetching related data until accessed (saves memory when data isn't needed), while eager loading fetches everything upfront (prevents N+1 when you know you'll need the data); choose based on your access patterns.

<?php // Lazy Loading - loads comments only when accessed class Post { private ?array $comments = null; public function getComments(): array { if ($this->comments === null) { $this->comments = $this->loadComments(); // Query fires here } return $this->comments; } } // Eager Loading - loads everything upfront (Eloquent example) $posts = Post::with(['comments', 'author'])->get();
┌────────────────────┬─────────────────────┐
│ Lazy Loading │ Eager Loading │
├────────────────────┼─────────────────────┤
│ Memory efficient │ Fewer queries │
│ Risk of N+1 │ Higher initial load │
│ On-demand queries │ Predictable timing │
└────────────────────┴─────────────────────┘

Connection Pooling

Connection pooling maintains a cache of reusable database connections, avoiding the overhead of establishing new connections (authentication, SSL handshake, etc.); PHP-FPM has persistent connections, but true pooling requires external tools like PgBouncer or ProxySQL.

<?php // PDO persistent connection (basic pooling) $pdo = new PDO( 'mysql:host=localhost;dbname=app', 'user', 'pass', [PDO::ATTR_PERSISTENT => true] // Reuse connection across requests );
┌─────────────────────────────────────────────────────────┐
│ Connection Pooling │
│ │
│ PHP-FPM Workers ProxySQL Database │
│ ┌─────┐ ┌───────┐ ┌──────┐ │
│ │ W1 │────┐ │ │ │ │ │
│ ├─────┤ │ │ Pool │──────────│ DB │ │
│ │ W2 │────┼───────────►│ of │──────────│ │ │
│ ├─────┤ │ │ Conns │──────────│ │ │
│ │ W3 │────┘ │ │ │ │ │
│ └─────┘ └───────┘ └──────┘ │
│ (50 workers → 10 DB connections) │
└─────────────────────────────────────────────────────────┘

Async Processing

Offload time-consuming tasks (emails, image processing, API calls) to background workers using message queues, allowing HTTP requests to return immediately; this dramatically improves user-perceived performance and allows retry logic for failures.

<?php // Controller - push to queue and return immediately class OrderController { public function create(Request $request): Response { $order = Order::create($request->all()); // Async - don't wait for these Queue::push(new SendOrderConfirmation($order)); Queue::push(new UpdateInventory($order)); Queue::push(new NotifyWarehouse($order)); return response()->json($order, 201); // Returns in ~50ms } }
┌────────────────────────────────────────────────────────┐
│ Request ──► Create Order ──► Queue Jobs ──► Response │
│ │ │
│ ┌─────────────────────┘ │
│ ▼ │
│ ┌─────────┐ ┌────────────────────────────┐ │
│ │ Queue │────►│ Worker: Email, SMS, etc. │ │
│ └─────────┘ └────────────────────────────┘ │
└────────────────────────────────────────────────────────┘

Load Balancing Strategies

Distribute incoming traffic across multiple PHP servers using algorithms like Round Robin (equal distribution), Least Connections (send to least busy), or IP Hash (sticky sessions); implement via Nginx, HAProxy, or cloud load balancers.

┌──────────────────────────────────────────────────────────┐
│ Load Balancing Strategies │
│ │
│ Clients ───► Load Balancer ─┬──► PHP Server 1 │
│ ├──► PHP Server 2 │
│ └──► PHP Server 3 │
│ │
│ Round Robin: 1→2→3→1→2→3 (equal distribution) │
│ Least Conns: Route to server with fewest active │
│ IP Hash: Same client → same server (sessions) │
│ Weighted: Server A(70%) → Server B(30%) │
└──────────────────────────────────────────────────────────┘
# nginx.conf upstream php_servers { least_conn; server 10.0.0.1:9000 weight=3; server 10.0.0.2:9000 weight=2; server 10.0.0.3:9000 weight=1; }

Horizontal vs Vertical Scaling

Vertical scaling (scale up) adds more CPU/RAM to existing servers—simpler but has limits; horizontal scaling (scale out) adds more servers—requires stateless design and shared sessions, but offers unlimited growth and better fault tolerance.

┌─────────────────────────────────────────────────────────┐
│ Vertical Scaling Horizontal Scaling │
│ (Scale Up) (Scale Out) │
│ │
│ ┌───────────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ S1 │ │ S2 │ │ S3 │ │
│ │ BIGGER │ └────┘ └────┘ └────┘ │
│ │ SERVER │ ┌────┐ ┌────┐ ┌────┐ │
│ │ │ │ S4 │ │ S5 │ │ S6 │ │
│ └───────────┘ └────┘ └────┘ └────┘ │
│ │
│ ✅ Simple ✅ Unlimited scaling │
│ ✅ No code changes ✅ Fault tolerant │
│ ❌ Hardware limits ❌ Needs stateless app │
│ ❌ Single point failure ❌ More complex │
└─────────────────────────────────────────────────────────┘

PHP Preloading

Preloading (PHP 7.4+) loads specified PHP files into OPcache at server startup, making classes immediately available to all requests without file I/O or compilation; ideal for frameworks and libraries that never change between deploys.

<?php // preload.php - executed once at PHP-FPM start $files = [ '/app/vendor/autoload.php', '/app/src/Core/Container.php', '/app/src/Core/Router.php', // ... frequently used classes ]; foreach ($files as $file) { opcache_compile_file($file); }
; php.ini opcache.preload=/app/preload.php opcache.preload_user=www-data
Before: Request → Autoload → Read File → Compile → Execute
After: Request → Execute (classes already in memory)

FFI (Foreign Function Interface)

FFI (PHP 7.4+) allows calling C functions and using C data structures directly from PHP without writing PHP extensions—useful for accessing system libraries, performance-critical code, or reusing existing C libraries.

<?php // Call C standard library functions directly $ffi = FFI::cdef(" int printf(const char *format, ...); int rand(void); void srand(unsigned int seed); ", "libc.so.6"); $ffi->srand(time()); echo $ffi->rand(); // Call C's rand() function // Use libcurl, ImageMagick, or any C library! $ffi = FFI::cdef(" typedef struct { int x; int y; } Point; ", "libgeometry.so"); $point = $ffi->new("Point"); $point->x = 10; $point->y = 20;

Asynchronous PHP

Swoole

Swoole is a high-performance coroutine-based async framework that turns PHP into a long-running application server, providing built-in support for HTTP/WebSocket servers, coroutines, connection pools, and can handle 100K+ concurrent connections.

<?php use Swoole\Http\Server; use Swoole\Http\Request; use Swoole\Http\Response; $server = new Server("0.0.0.0", 9501); $server->on("request", function (Request $request, Response $response) { // Each request runs in a coroutine $result = Co\run(function() { // These run concurrently, not sequentially! $data1 = Co::exec(fn() => file_get_contents('http://api1.com')); $data2 = Co::exec(fn() => file_get_contents('http://api2.com')); return [$data1, $data2]; }); $response->end(json_encode($result)); }); $server->start();

ReactPHP

ReactPHP is a low-level library for event-driven, non-blocking I/O in PHP, implementing the reactor pattern with a single-threaded event loop; it's the foundation for building async HTTP clients, servers, and database connections.

<?php require 'vendor/autoload.php'; $loop = React\EventLoop\Loop::get(); // Non-blocking HTTP server $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { return React\Http\Message\Response::plaintext("Hello World!\n"); }); $socket = new React\Socket\SocketServer('127.0.0.1:8080'); $http->listen($socket); echo "Server running at http://127.0.0.1:8080\n"; $loop->run();
┌─────────────────────────────────────────────────────────┐
│ ReactPHP Event Loop │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Event Loop │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Timers │ │ Streams │ │ Signals │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ │ └───────────┼───────────┘ │ │
│ │ ▼ │ │
│ │ Process Events │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

Amp

Amp is an async framework that uses PHP Fibers (8.1+) or generators for coroutines, providing a cleaner API than callbacks; it includes async HTTP client, database pools, and file I/O, making concurrent code look synchronous.

<?php use Amp\Http\Client\HttpClientBuilder; use function Amp\async; use function Amp\await; // Concurrent HTTP requests - looks synchronous! $client = HttpClientBuilder::buildDefault(); $futures = [ async(fn() => $client->request(new Request('https://api1.com'))), async(fn() => $client->request(new Request('https://api2.com'))), async(fn() => $client->request(new Request('https://api3.com'))), ]; // All 3 requests execute concurrently $responses = await($futures); // ~200ms instead of ~600ms

RoadRunner

RoadRunner is a high-performance PHP application server written in Go that keeps PHP workers alive between requests, eliminating bootstrap overhead; it supports HTTP, gRPC, queues, and can improve throughput by 10-40x compared to PHP-FPM.

# .rr.yaml version: "3" server: command: "php worker.php" http: address: "0.0.0.0:8080" pool: num_workers: 4 max_jobs: 64 # Restart worker after 64 requests
<?php // worker.php - runs in a loop, not per-request use Spiral\RoadRunner\Http\PSR7Worker; $worker = new PSR7Worker(...); while ($req = $worker->waitRequest()) { try { // Your app code - framework already bootstrapped! $response = $app->handle($req); $worker->respond($response); } catch (\Throwable $e) { $worker->getWorker()->error((string)$e); } }

PHP Fibers

Fibers (PHP 8.1+) are lightweight, stackful coroutines that can be suspended and resumed, enabling async frameworks to write non-blocking code that looks synchronous; they're low-level primitives typically wrapped by libraries like Amp or Revolt.

<?php $fiber = new Fiber(function (): void { $value = Fiber::suspend('fiber suspended'); echo "Resumed with: $value\n"; }); $result = $fiber->start(); // Output: nothing yet echo "Got: $result\n"; // Output: Got: fiber suspended $fiber->resume('hello world'); // Output: Resumed with: hello world
┌─────────────────────────────────────────────────────────┐
│ Fiber Lifecycle │
│ │
│ start() ──► Running ──► suspend() ──► Suspended │
│ │ │
│ ▼ │
│ Terminated ◄── return/throw ◄── resume() ◄┘ │
└─────────────────────────────────────────────────────────┘

Parallel Extension

The parallel extension provides true multi-threading in PHP with a simple API for running tasks in parallel across CPU cores; it uses a thread-safe architecture where data is copied between threads (no shared state).

<?php use parallel\Runtime; use parallel\Future; // Create runtimes (threads) $runtime1 = new Runtime(); $runtime2 = new Runtime(); // Run tasks in parallel $future1 = $runtime1->run(function() { return heavy_computation_1(); }); $future2 = $runtime2->run(function() { return heavy_computation_2(); }); // Wait for results $result1 = $future1->value(); // Both completed in parallel $result2 = $future2->value();

Event Loops

The event loop is the heart of async programming—it continuously checks for I/O events, timers, and signals, executing callbacks when events occur; this allows a single thread to handle thousands of concurrent connections efficiently.

┌─────────────────────────────────────────────────────────┐
│ Event Loop Cycle │
│ │
│ ┌──────────────────────┐ │
│ │ Check Timers │ │
│ └──────────┬───────────┘ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ Poll I/O Events │ ◄── epoll/kqueue │
│ └──────────┬───────────┘ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ Execute Callbacks │ │
│ └──────────┬───────────┘ │
│ │ │
│ └──────── Loop ─────────────┐ │
│ │ │
│ ┌──────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

Promises and Coroutines

Promises represent a value that may be available now, later, or never, enabling chained async operations; coroutines are functions that can suspend execution and resume later, making async code read like synchronous code.

<?php // Promise-based (ReactPHP style) $promise = fetchUserAsync(1) ->then(fn($user) => fetchOrdersAsync($user->id)) ->then(fn($orders) => processOrders($orders)) ->catch(fn($error) => logError($error)); // Coroutine-based (Amp/Fiber style) - cleaner! $user = await(fetchUserAsync(1)); $orders = await(fetchOrdersAsync($user->id)); $result = processOrders($orders);
Promise States:
┌──────────┐ resolve() ┌───────────┐
│ Pending │────────────────►│ Fulfilled │
└──────────┘ └───────────┘
     │
     │ reject() ┌───────────┐
     └──────────────────────►│ Rejected │
                             └───────────┘

Non-blocking I/O

Non-blocking I/O allows operations (network, file, database) to return immediately without waiting for completion; the program continues executing other code and gets notified via callbacks or polling when data is ready.

<?php // Blocking - waits 3 seconds sequentially = 9 seconds total $api1 = file_get_contents('http://slow-api-1.com'); // 3s $api2 = file_get_contents('http://slow-api-2.com'); // 3s $api3 = file_get_contents('http://slow-api-3.com'); // 3s // Non-blocking (ReactPHP) - all run concurrently = 3 seconds total $browser = new React\Http\Browser(); $promises = [ $browser->get('http://slow-api-1.com'), $browser->get('http://slow-api-2.com'), $browser->get('http://slow-api-3.com'), ]; $results = await(React\Promise\all($promises));

WebSockets

WebSockets provide full-duplex, persistent connections between client and server, ideal for real-time features like chat, notifications, and live updates; PHP handles these via Swoole, Ratchet, or external services like Pusher.

<?php // Ratchet WebSocket Server use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; class Chat implements MessageComponentInterface { protected $clients; public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); } public function onMessage(ConnectionInterface $from, $msg) { foreach ($this->clients as $client) { if ($from !== $client) { $client->send($msg); // Broadcast to all } } } }
┌──────────┐ Persistent Connection ┌──────────┐
│ Client │◄──────────────────────────────────────►│ Server │
└──────────┘ ◄── Messages flow both ways ──► └──────────┘

Message Queues and Event Streaming

RabbitMQ

RabbitMQ is a robust message broker implementing AMQP protocol, supporting complex routing patterns (direct, topic, fanout), message acknowledgments, and persistence; use the php-amqplib library for integration.

<?php use PhpAmqpLib\Connection\AMQPStreamConnection; use PhpAmqpLib\Message\AMQPMessage; $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest'); $channel = $connection->channel(); $channel->queue_declare('orders', false, true, false, false); // Publish $msg = new AMQPMessage(json_encode(['order_id' => 123])); $channel->basic_publish($msg, '', 'orders'); // Consume $channel->basic_consume('orders', '', false, false, false, false, function ($msg) { $order = json_decode($msg->body); processOrder($order); $msg->ack(); } );

Apache Kafka

Kafka is a distributed event streaming platform designed for high-throughput, fault-tolerant data pipelines; it stores events in ordered, immutable logs (topics) with configurable retention, enabling event sourcing and stream processing at scale.

<?php // Using rdkafka extension $conf = new RdKafka\Conf(); $conf->set('metadata.broker.list', 'localhost:9092'); // Producer $producer = new RdKafka\Producer($conf); $topic = $producer->newTopic('user-events'); $topic->produce(RD_KAFKA_PARTITION_UA, 0, json_encode(['event' => 'user.created'])); // Consumer $consumer = new RdKafka\KafkaConsumer($conf); $consumer->subscribe(['user-events']); while (true) { $message = $consumer->consume(1000); if ($message->err === RD_KAFKA_RESP_ERR_NO_ERROR) { processEvent($message->payload); } }
┌───────────────────────────────────────────────────────┐
│ Kafka Topology │
│ │
│ Producers ──► Topic (Partitions) ──► Consumer Groups │
│ │
│ ┌─────┐ ┌─────────────────┐ ┌───────────┐ │
│ │ P1 │──────►│ Partition 0 │────►│ Consumer │ │
│ ├─────┤ ├─────────────────┤ │ Group A │ │
│ │ P2 │──────►│ Partition 1 │────►│ │ │
│ ├─────┤ ├─────────────────┤ └───────────┘ │
│ │ P3 │──────►│ Partition 2 │────►│ Consumer │ │
│ └─────┘ └─────────────────┘ │ Group B │ │
└───────────────────────────────────────────────────────┘

Amazon SQS

Amazon SQS is a fully managed message queue service offering standard (at-least-once, best-effort ordering) and FIFO (exactly-once, ordered) queues; it scales automatically and integrates with Lambda for serverless processing.

<?php use Aws\Sqs\SqsClient; $client = new SqsClient([ 'region' => 'us-east-1', 'version' => 'latest' ]); // Send message $client->sendMessage([ 'QueueUrl' => 'https://sqs.us-east-1.amazonaws.com/123/my-queue', 'MessageBody' => json_encode(['task' => 'process_image', 'id' => 42]), 'DelaySeconds' => 10 // Delay delivery ]); // Receive and delete $result = $client->receiveMessage(['QueueUrl' => $queueUrl, 'MaxNumberOfMessages' => 10]); foreach ($result['Messages'] as $message) { process($message['Body']); $client->deleteMessage(['QueueUrl' => $queueUrl, 'ReceiptHandle' => $message['ReceiptHandle']]); }

Redis Pub/Sub

Redis Pub/Sub provides lightweight, fire-and-forget messaging where publishers send messages to channels and all subscribers receive them instantly; messages aren't persisted, so it's ideal for real-time notifications rather than task queues.

<?php // Publisher $redis = new Redis(); $redis->connect('127.0.0.1'); $redis->publish('notifications', json_encode(['user' => 1, 'msg' => 'Hello'])); // Subscriber (runs in separate process) $redis = new Redis(); $redis->subscribe(['notifications'], function ($redis, $channel, $message) { echo "Received on $channel: $message\n"; });
┌─────────────────────────────────────────────────────────┐
│ Redis Pub/Sub │
│ │
│ Publisher ──► Channel: "notifications" ──┬─► Sub 1 │
│ ├─► Sub 2 │
│ └─► Sub 3 │
│ │
│ Note: No persistence - missed messages are lost! │
└─────────────────────────────────────────────────────────┘

Beanstalkd

Beanstalkd is a simple, fast work queue focused on job scheduling with features like delayed jobs, priority, time-to-run limits, and job burial for failures; it's lighter than RabbitMQ and perfect for background task processing.

<?php use Pheanstalk\Pheanstalk; $pheanstalk = Pheanstalk::create('127.0.0.1'); // Producer $pheanstalk ->useTube('emails') ->put( json_encode(['to' => 'user@example.com', 'template' => 'welcome']), priority: 1024, delay: 0, ttr: 60 // 60 seconds to process ); // Worker while ($job = $pheanstalk->watchOnly('emails')->reserve()) { try { $data = json_decode($job->getData(), true); sendEmail($data); $pheanstalk->delete($job); } catch (Exception $e) { $pheanstalk->bury($job); // Move to buried queue for inspection } }

Queue Workers

Queue workers are long-running processes that continuously poll queues for jobs, process them, and acknowledge completion; design them to be stateless, handle failures gracefully, and implement proper shutdown signals.

<?php // Supervisor-managed worker class QueueWorker { private bool $shouldRun = true; public function __construct() { pcntl_signal(SIGTERM, fn() => $this->shouldRun = false); } public function run(): void { while ($this->shouldRun) { pcntl_signal_dispatch(); $job = $this->queue->pop(); if ($job) { try { $job->handle(); $this->queue->ack($job); } catch (Throwable $e) { $this->queue->nack($job); $this->logger->error($e); } } else { sleep(1); // No jobs, wait before polling } } } }

Dead Letter Queues

Dead Letter Queues (DLQ) store messages that failed processing after maximum retry attempts, preventing poison messages from blocking the queue; regularly monitor and analyze DLQ to identify bugs or invalid data.

┌────────────────────────────────────────────────────────┐
│ Dead Letter Queue Flow │
│ │
│ Main Queue ──► Worker ──► Success ──► Complete │
│ │ │
│ └──► Failure ──► Retry (1,2,3...) ──► Max? │
│ │ │
│ ┌────────────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Dead Letter Q │ ◄── Alerting │
│ │ (for analysis) │ │
│ └──────────────────┘ │
└────────────────────────────────────────────────────────┘
<?php // Laravel example class ProcessOrder implements ShouldQueue { public $tries = 3; public $backoff = [10, 60, 300]; // Retry delays public function failed(Throwable $e): void { // Automatically moves to failed_jobs table (DLQ) Log::error('Order processing failed permanently', [ 'order_id' => $this->order->id, 'error' => $e->getMessage() ]); } }

Message Serialization

Message serialization encodes data for queue transport; use JSON for interoperability, MessagePack for efficiency, or Protobuf for schema validation; avoid PHP's serialize() in distributed systems due to security and compatibility issues.

<?php $order = ['id' => 123, 'items' => [...]]; // JSON - human-readable, universal $json = json_encode($order); // MessagePack - binary, 30-50% smaller than JSON $msgpack = msgpack_pack($order); // Protobuf - schema-defined, strongly typed $proto = new OrderMessage(); $proto->setId(123); $binary = $proto->serializeToString();
┌──────────────────────────────────────────────────────────┐
│ Serialization Comparison │
├────────────┬────────────┬────────────┬──────────────────┤
│ Format │ Size │ Speed │ Interop │
├────────────┼────────────┼────────────┼──────────────────┤
│ JSON │ Large │ Medium │ ✅ Excellent │
│ MsgPack │ Small │ Fast │ ✅ Good │
│ Protobuf │ Smallest │ Fastest │ ✅ Schema req. │
│ serialize │ Large │ Fast │ ❌ PHP only │
└────────────┴────────────┴────────────┴──────────────────┘

Search and Indexing

Elasticsearch Integration

Elasticsearch is a distributed search and analytics engine that indexes documents as JSON; use the elasticsearch/elasticsearch client for PHP, design indexes with proper mappings, and optimize queries with filters and aggregations.

<?php use Elastic\Elasticsearch\ClientBuilder; $client = ClientBuilder::create()->setHosts(['localhost:9200'])->build(); // Index a document $client->index([ 'index' => 'products', 'id' => '123', 'body' => [ 'name' => 'MacBook Pro', 'description' => 'Powerful laptop for developers', 'price' => 2499, 'tags' => ['electronics', 'computers'] ] ]); // Search with query DSL $results = $client->search([ 'index' => 'products', 'body' => [ 'query' => [ 'bool' => [ 'must' => ['match' => ['description' => 'developers']], 'filter' => ['range' => ['price' => ['lte' => 3000]]] ] ] ] ]);

Algolia

Algolia is a hosted search-as-a-service providing millisecond search with typo-tolerance, faceting, and geo-search out of the box; it's ideal when you want powerful search without managing infrastructure.

<?php use Algolia\AlgoliaSearch\SearchClient; $client = SearchClient::create('APP_ID', 'API_KEY'); $index = $client->initIndex('products'); // Index records $index->saveObjects([ ['objectID' => '1', 'name' => 'iPhone', 'price' => 999], ['objectID' => '2', 'name' => 'iPad', 'price' => 799], ]); // Search with instant results $results = $index->search('iphone', [ 'filters' => 'price < 1000', 'hitsPerPage' => 10 ]); // Returns in ~20ms with typo tolerance, highlighting, etc.

Meilisearch

Meilisearch is a fast, open-source search engine focused on developer experience; it offers typo-tolerance, faceted search, and simple setup—perfect for applications needing Algolia-like features without vendor lock-in.

<?php use Meilisearch\Client; $client = new Client('http://localhost:7700', 'masterKey'); $index = $client->index('movies'); // Add documents $index->addDocuments([ ['id' => 1, 'title' => 'The Matrix', 'genre' => 'sci-fi'], ['id' => 2, 'title' => 'Interstellar', 'genre' => 'sci-fi'], ]); // Search - typo-tolerant by default $results = $index->search('matix'); // Finds "Matrix" despite typo
┌─────────────────────────────────────────────────────────┐
│ Search Engine Comparison │
├─────────────────┬──────────────┬────────────┬──────────┤
│ │ Elasticsearch│ Algolia │Meilisearch│
├─────────────────┼──────────────┼────────────┼──────────┤
│ Hosting │ Self/Cloud │ Hosted │ Self │
│ Complexity │ High │ Low │ Low │
│ Cost │ Infra/Staff │ Per search │ Free │
│ Full-text │ ✅ │ ✅ │ ✅ │
│ Analytics │ ✅ │ ✅ │ ❌ │
└─────────────────┴──────────────┴────────────┴──────────┘

Sphinx

Sphinx is a mature, full-text search server optimized for indexing database content; it integrates tightly with MySQL/PostgreSQL and excels at searching large datasets with SQL-like query syntax.

<?php // SphinxQL (SQL-like syntax) $sphinxql = new PDO('mysql:host=127.0.0.1;port=9306'); $stmt = $sphinxql->query(" SELECT id, WEIGHT() as relevance FROM products_index WHERE MATCH('laptop computer') ORDER BY relevance DESC LIMIT 20 "); $results = $stmt->fetchAll();
┌───────────────────────────────────────────────────────┐
│ Sphinx Architecture │
│ │
│ MySQL ──► indexer ──► Index Files ◄── searchd │
│ │ │
│ ▼ │
│ PHP Client │
│ (SphinxQL/API) │
└───────────────────────────────────────────────────────┘

Full-text search finds documents by analyzing text content rather than exact matches; it involves tokenization, stemming (running→run), stop word removal, and relevance scoring—implement via dedicated engines or database features like MySQL FULLTEXT or PostgreSQL tsvector.

<?php // MySQL FULLTEXT (simple cases) $stmt = $pdo->query(" SELECT *, MATCH(title, content) AGAINST('php performance' IN NATURAL LANGUAGE MODE) as score FROM articles WHERE MATCH(title, content) AGAINST('php performance' IN NATURAL LANGUAGE MODE) ORDER BY score DESC "); // PostgreSQL full-text $stmt = $pdo->query(" SELECT *, ts_rank(search_vector, query) as rank FROM articles, plainto_tsquery('english', 'php performance') query WHERE search_vector @@ query ORDER BY rank DESC ");
┌─────────────────────────────────────────────────────────┐
│ Full-text Search Pipeline │
│ │
│ "Running quickly" ──► Tokenize ──► ["running","quickly"]
│ │ │
│ ┌──────────────────┘ │
│ ▼ │
│ Normalize ──► ["run", "quick"] │
│ │ │
│ ▼ │
│ Build Index ──► Inverted Index │
│ │
│ Query: "run" ──► Match ──► Score ──► Ranked Results │
└─────────────────────────────────────────────────────────┘

This completes the advanced PHP topics. Each of these areas requires hands-on practice—start with profiling your current code, then progressively add async processing and message queues as your application scales! 🚀