Overview of amphp/amp
3.xamphp/amp package specifically provides Future and Cancellation primitives. It uses Revolt as the underlying event loop implementation rather than shipping its own.repository·3.x·Indexed 26 days ago
https://github.com/amphp/ampA library providing fundamental primitives for asynchronous programming in PHP, such as Futures and Cancellations. It leverages PHP 8.1 Fibers to enable non-blocking, concurrent code with a synchronous-looking syntax and utilizes revolt/event-loop as its underlying event loop implementation.
amphp/amp package specifically provides Future and Cancellation primitives. It uses Revolt as the underlying event loop implementation rather than shipping its own.Amp\async(). To retrieve the result of a concurrent task, use Future::await().Install the core amphp/amp package using Composer. Since amphp/amp relies on an event loop for scheduling, it is highly recommended to explicitly require revolt/event-loop as well.
composer require amphp/amp
composer require revolt/event-loopCoroutines in Amp are interruptible functions implemented using PHP Fibers. You can start a new coroutine (a new fiber with an independent call stack) using Amp\async(). This allows you to run asynchronous tasks without the boilerplate required by older generator-based implementations.
Callbacks registered on the Revolt event-loop are automatically run as coroutines, making it safe to suspend within them.
<?php
require __DIR__ . '/vendor/autoload.php';
Amp\async(function () {
print '++ Executing callback passed to async()' . PHP_EOL;
Amp\[delay(3);
print '++ Finished callback passed to async()' . PHP_EOL;
});
print '++ Suspending to event loop...' . PHP_EOL;
Amp\delay(5);
print '++ Script end' . PHP_EOL;Amp\delay(float $timeout, bool $reference = true, ?Cancellation $cancellation = null): voidSuspends the current coroutine until the timeout elapses or the cancellation is triggered.
Amp\trapSignal(int|array $signals, bool $reference = true, ?Cancellation $cancellation = null): intSuspends the current coroutine until one of the specified signals is received. Returns the signal number.
Amp\now(): floatReturns a high-resolution time relative to an arbitrary point, useful for calculating time differences independent of wall-time.
Amp\weakClosure(Closure $closure): ClosureWraps a closure in a weak-reference to any $this object it holds. This prevents circular references that delay garbage collection.
The Amp\Interval class registers a callback in the event-loop that is invoked within a new coroutine every specified number of seconds. The interval runs until Interval::disable() is called or the object is destroyed.
Tip: When storing an Interval inside another class, use Amp\weakClosure() for the callback to avoid circular references that prevent garbage collection.
// Creates a callback which is invoked every 0.5s
// unless disabled or the object is destroyed.
$interval = new Interval(0.5, function (): void {
// ...
});
// Disable the repeating timer, stopping future
// invocations until enabled again.
$interval->disable();
// Enable the repeating timer. The callback will
// not be invoked until the given timeout has elapsed.
$interval->enable();A Future represents the eventual result of an asynchronous operation. It can be in one of three states: Completed (success), Errored (exception thrown), or Pending.
await(): Suspends the current coroutine until the future completes or errors. Returns the result or throws the exception.map(Closure $map): Future: Attaches a callback for successful completion. Returns a new future with the callback's result.catch(Closure $catch): Future: Attaches a callback for errors. Returns a new future with the caught exception handled.finally(Closure $finally): Future: Attaches a callback that always runs. Returns a new future with the original result or error.An Amp\DeferredFuture is used to manually complete a pending Future. This is an advanced API typically used for internal state within an operation.
DeferredFuture instance.$deferred->getFuture() to the caller.$deferred->complete($value) or $deferred->error($throwable) to resolve the future.Warning: Do not pass DeferredFuture objects around; only pass the Future they produce.
<?php // Example async producer using DeferredFuture
use Amp\Future;
use Revolt\EventLoop;
require __DIR__ . '/vendor/autoload.php';
function asyncMultiply(int $x, int $y): Future
{
$deferred = new Amp\DeferredFuture;
// Complete the async result one second from now
EventLoop::delay(1, function () use ($deferred, $x, $y) {
$deferred->complete($x * $y);
});
return $deferred->getFuture();
}
$future = asyncMultiply(6, 7);
$result = $future->await();
var_dump($result); // int(42)Use combinators to manage multiple concurrent asynchronous operations:
Amp\Future\await($iterable, $cancellation): Awaits all futures in an iterable. If any fail, the operation aborts with that exception. Returns an array of results.Amp\Future\awaitAnyN($count, $iterable, $cancellation): Returns once exactly $count instances complete successfully. Tolerates individual errors.Amp\Future\awaitAll($iterable, $cancellation): Awaits all futures and returns an array in the format [$errors, $values].Amp\Future\awaitFirst($iterable, $cancellation): Unwraps the first completed Future (success or error).Amp\Future\awaitAny($iterable, $cancellation): Unwraps the first successfully completed Future.<?php
use Amp\Future;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
require __DIR__ . '/vendor/autoload.php';
$httpClient = HttpClientBuilder::buildDefault();
$uris = [
"google" => "https://www.google.com",
"news" => "https://news.google.com",
"bing" => "https://www.bing.com",
"yahoo" => "https://www.yahoo.com",
];
try {
$responses = Future\await(array_map(function ($uri) use ($httpClient) {
return Amp\async(fn () => $httpClient->request(new Request($uri, 'HEAD')));
}, $uris));
foreach ($responses as $key => $response) {
printf(
"%s | HTTP/%s %d %s\n",
$key,
$response->getProtocolVersion(),
$response->getStatus(),
$response->getReason()
);
}
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}Operations can support cancellation by accepting a Cancellation object. Use $cancellation->throwIfRequested() to fail the operation with a CancelledException when requested, or use $cancellation->subscribe() to react to cancellation events.
Amp\TimeoutCancellation: Automatically cancels after a specified number of seconds.Amp\SignalCancellation: Automatically cancels when a specific process signal (e.g., SIGINT) is received.Amp\DeferredCancellation: Allows manual cancellation via $deferredCancellation->cancel(). Best for custom logic.Amp\NullCancellation: A non-cancelling object used to avoid null checks.Amp\CompositeCancellation: Combines multiple cancellations; if any one is cancelled, the composite is cancelled.Amp\TimeoutException is thrown when an operation exceeds its allotted time, specifically when a TimeoutCancellation expires. You can catch this exception to handle scenarios where an asynchronous operation failed to complete within the expected duration.Amp\SignalException is thrown when an asynchronous operation is interrupted by a system signal (e.g., SIGINT) via a SignalCancellation. It is typically used as the underlying cause for a CancelledException when a SignalCancellation is triggered. You can catch this exception to handle graceful shutdowns or interruptions caused by external signals.