Slide 1

Slide 1 text

Asynchronous Awesome php[tek] 2023 • Eric Mann

Slide 2

Slide 2 text

Traditional PHP Lifecycle Singular Execution No Events Blocking I/O

Slide 3

Slide 3 text

Asynchronous Operations Parallel Concurrent Doing two things simultaneously Doing two things independently

Slide 4

Slide 4 text

Why Bother? Image Processing Render or process large graphics in the background Data processing Manipulate “big data” in parallel with other tasks API Juggling Rely on multiple APIs at once and collate the results Provide Feedback Parallelize long-running tasks to update UI with progress

Slide 5

Slide 5 text

Asynchronous PHP Landscape

Slide 6

Slide 6 text

Fibers Native concurrency support within PHP 8.1+ 01

Slide 7

Slide 7 text

PHP Fibers Fibers natively abstract a separate thread with an independent call stack. Your application can control two completely separate processes at once. Fibers are not run in parallel but allow for concurrent processing of data. They provide a helpful abstraction for jumping between two different operations without losing context.

Slide 8

Slide 8 text

PHP Fibers In Practice With the Fiber class, you can instantiate a separate call stack. This creates an entirely independent thread of operation that you can control from your application. $fiber = new Fiber(function (): void { $value = Fiber::suspend('fiber'); echo "Value used to resume fiber: ", $value, "\n"; }); $value = $fiber->start(); echo "Value from fiber suspending: ", $value, "\n"; $fiber->resume('test');

Slide 9

Slide 9 text

PHP Fibers In Practice With the Fiber class, you can instantiate a separate call stack. This creates an entirely independent thread of operation that you can control from your application. %> php fibers.php Value from fiber suspending: fiber Value used to resume fiber: test

Slide 10

Slide 10 text

PHP Fibers Independent Call Stacks Fibers empower the quick creation of new/clean/separate call stacks in which variables and state are not shared. You can easily carve off parts of your application to run concurrently! Fibers Will Not Persist Unlike an explicitly forked child process, a Fiber only exists so long as the parent application is running. If the application exits (or crashes) the Fiber ends as well. Use an Abstraction While you certainly can use Fibers in userland, avoid using them directly. Instead, leverage frameworks like AMPHP that abstract the interface away into cleaner concepts like coroutines.

Slide 11

Slide 11 text

AMPHP Asynchronous, multi-tasking PHP library support 02

Slide 12

Slide 12 text

AMPHP Composer-installable framework that provides event-based concurrency to PHP in userland. The full event loop allows for effective management of Promise and Coroutine abstractions. Likewise, AMPHP abstracts the native PHP Fiber interfaces to make your code future-proof and efficient to write.

Slide 13

Slide 13 text

AMPHP In Practice $future1 = async(function () { echo 'Hello '; delay(2); echo 'the future! '; }); $future2 = async(function () { echo 'World '; delay(1); echo 'from '; }); echo "Let's start: "; $future1->await(); $future2->await(); Asynchronous calls are entirely non-blocking, allowing the program to either continue operating, or explicitly “await” for the result of the otherwise asynchronous function call.

Slide 14

Slide 14 text

AMPHP In Practice Asynchronous calls are entirely non-blocking, allowing the program to either continue operating, or explicitly “await” for the result of the otherwise asynchronous function call.

Slide 15

Slide 15 text

RabbitMQ Multi-language support for sending messages between apps 03

Slide 16

Slide 16 text

RabbitMQ RabbitMQ is an open source message queue. It empowers clients in multiple languages to create and dispatch messages to a simple queue system that can then be read and processed independently by other, completely asynchronous workers. A production environment might even use clients and workers leveraging entirely different programming languages!

Slide 17

Slide 17 text

RabbitMQ In Practice use PhpAmqpLib\Connection\AMQPStreamConnection; $conn = new AMQPStreamConnection('localhost', 5762, 'u', 'p'); $channel = $conn->channel(); $channel->queue_declare('default', 0, 0, 0, 0); $cb = function($msg) { $data = json_decode($msg->body, true); $to = $data['to']; $message = wordwrap($data['message'], 70, "PHP_EOL"); $headers = 'From: worker.local'; mail($to, 'Message', $message, $headers); $msg->ack(); }; $channel->basic_consume('default', '', 0, 0, 0, 0, $cb); while(count($channel->callbacks)) $channel->wait(); The first step is to create a queue and attach at least one worker that can process messages on that queue. Queues are created dynamically upon their first reference.

Slide 18

Slide 18 text

RabbitMQ In Practice use PhpAmqpLib\Connection\AMQPStreamConnection; use PhpAmqpLib\Message\AMQPMessage; $conn = new AMQPStreamConnection('localhost', 5672, 'u', 'p'); $channel = $conn->channel(); $channel->queue_declare('default'); $message = ['message' => 'Welcome to the team!']; $teammates = ['[email protected]', '[email protected]']; foreach($teammates as $employee) { $message['to'] = $employee; $msg = new AMQPMessage(json_encode($message)); $channel->basic_publish($msg, '', 'default'); } $channel->close(); $connection->close(); Next, publish messages into the queue for the workers to retrieve and process.

Slide 19

Slide 19 text

RabbitMQ App Queue Worker Worker Worker

Slide 20

Slide 20 text

RabbitMQ App Queue Worker Worker Worker Queue

Slide 21

Slide 21 text

… and Friends Native extensions to PHP: Open Swoole, RoadRunner, etc… 04

Slide 22

Slide 22 text

Open Swoole Open Swoole is a PECL-installed low-level extension that provides an asynchronous framework for PHP. It natively implements promises and coroutines. As a native extension, it outperforms any userland promise/coroutine implementation. It also supports event-driven code and channels for subprocess communication.

Slide 23

Slide 23 text

Roadrunner Roadrunner is an alternative runtime for PHP, implemented in Golang. Uses the same interface you’re used to, but ships its own application server and asynchronous process manager. With Roadrunner, your entire application stays in memory. You can invoke atomic processes in parallel to the main application whenever necessary.

Slide 24

Slide 24 text

Laravel Octane Octane leverages either (Open) Swoole or RoadRunner to serve your Laravel application: ● First request stores the entire application in memory ● Subsequent requests are fast and efficient ● Handles 2-3x requests per second over Apache/Nginx alone

Slide 25

Slide 25 text

Thank you Any questions? [email protected]

Slide 26

Slide 26 text

Just released this week! Get your own copy in print or DRM-free digital!