8.1 KiB
AMPHP is a collection of event-driven libraries for PHP designed with fibers and concurrency in mind.
amphp/amp
specifically provides futures and cancellations as fundamental primitives for asynchronous programming.
We're now using Revolt instead of shipping an event loop implementation with amphp/amp
.
Amp makes heavy use of fibers shipped with PHP 8.1 to write asynchronous code just like synchronous, blocking code. In
contrast to earlier versions, there's no need for generator based coroutines or callbacks. Similar to threads, each
fiber has its own call stack, but fibers are scheduled cooperatively by the event loop. Use Amp\async()
to run things
concurrently.
Motivation
Traditionally, PHP follows a sequential execution model. The PHP engine executes one line after the other in sequential order. Often, however, programs consist of multiple independent sub-programs with can be executed concurrently.
If you query a database, you send the query and wait for the response from the database server in a blocking manner. Once you have the response, you can start doing the next thing. Instead of sitting there and doing nothing while waiting, we could already send the next database query, or do an HTTP call to an API. Let's make use of the time we usually spend on waiting for I/O!
Revolt allows such concurrent I/O operations. We keep the cognitive load low by avoiding callbacks.
Our APIs can be used like any other library, except that things also work concurrently, because we use non-blocking I/O under the hood.
Run things concurrently using Amp\async()
and await the result using Future::await()
where and when you need it!
There have been various techniques for implementing concurrency in PHP over the years, e.g. callbacks and generators shipped in PHP 5. These approaches suffered from the "What color is your function" problem, which we solved by shipping Fibers with PHP 8.1. They allow for concurrency with multiple independent call stacks.
Fibers are cooperatively scheduled by the event-loop, which is why they're also called coroutines. It's important to understand that only one coroutine is running at any given time, all other coroutines are suspended in the meantime.
You can compare coroutines to a computer running multiple programs using a single CPU core. Each program gets a timeslot to execute. Coroutines, however, are not preemptive. They don't get their fixed timeslot. They have to voluntarily give up control to the event loop.
Any blocking I/O function blocks the entire process while waiting for I/O. You'll want to avoid them. If you haven't read the installation guide, have a look at the Hello World example that demonstrates the effect of blocking functions. The libraries provided by AMPHP avoid blocking for I/O.
Installation
This package can be installed as a Composer dependency.
composer require amphp/amp
If you use this library, it's very likely you want to schedule events using Revolt, which you should require separately, even if it's automatically installed as a dependency.
composer require revolt/event-loop
These packages provide the basic building blocks for asynchronous / concurrent applications in PHP. We offer a lot of packages building on top of these, e.g.
amphp/byte-stream
providing a stream abstractionamphp/socket
providing a socket layer for UDP and TCP including TLSamphp/parallel
providing parallel processing to utilize multiple CPU cores and offload blocking operationsamphp/http-client
providing an HTTP/1.1 and HTTP/2 clientamphp/http-server
providing an HTTP/1.1 and HTTP/2 application serveramphp/mysql
andamphp/postgres
for non-blocking database access- and many more packages
Requirements
This package requires PHP 8.1+, no extensions required!
Extensions are only needed if your app necessitates a high numbers of concurrent socket connections, usually this limit is configured up to 1024 file descriptors.
Usage
Future
Cancellation
Every operation that supports cancellation accepts an instance of Cancellation
as argument.
Cancellations are objects that allow registering handlers to subscribe to cancellation requests.
These objects are passed down to sub-operations or have to be handled by the operation itself.
$cancellation->throwIfRequested()
can be used to fail the current operation with a CancelledException
once cancellation has been requested.
While throwIfRequested()
works well, some operations might want to subscribe with a callback instead. They can do so
using Cancellation::subscribe()
to subscribe any cancellation requests that might happen.
The caller creates a Cancellation
by using one of the implementations below.
Note
Cancellations are advisory only. A DNS resolver might ignore cancellation requests after the query has been sent as the response has to be processed anyway and can still be cached. An HTTP client might continue a nearly finished HTTP request to reuse the connection, but might abort a chunked encoding response as it cannot know whether continuing is actually cheaper than aborting.
TimeoutCancellation
A TimeoutCancellations
automatically cancels itself after the specified number of seconds.
request("...", new Amp\TimeoutCancellation(30));
SignalCancellation
A SignalCancellation
automatically cancels itself after a specified signal has been received by the current process.
request("...", new Amp\SignalCancellation(SIGINT));
DeferredCancellation
A DeferredCancellation
allows manual cancellation with the call of a method.
This is the preferred way if you need to register some custom callback somewhere instead of shipping your own implementation.
Only the caller has access to the DeferredCancellation
and can cancel the operation using DeferredCancellation::cancel()
.
$deferredCancellation = new Amp\DeferredCancellation();
// Register some custom callback somewhere
onSomeEvent(fn () => $deferredCancellation->cancel());
request("...", $deferredCancellation->getCancellation());
NullCancellation
A NullCancellation
will never be cancelled.
Cancellation is often optional, which is usually implemented by making the parameter nullable.
To avoid guards like if ($cancellation)
, a NullCancellation
can be used instead.
$cancellation ??= new NullCancellationToken();
CompositeCancellation
A CompositeCancellation
combines multiple independent cancellation objects. If any of these cancellations is cancelled, the CompositeCancellation
itself will be cancelled.
Versioning
amphp/amp
follows the semver semantic versioning specification like all other amphp
packages.
Compatible Packages
Compatible packages should use the amphp
topic on GitHub.
Security
If you discover any security related issues, please email me@kelunik.com
instead of using the
issue tracker.
License
The MIT License (MIT). Please see LICENSE
for more information.