[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-8401":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":17,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":32,"readmeContent":33,"aiSummary":34,"trendingCount":16,"starSnapshotCount":16,"syncStatus":35,"lastSyncTime":36,"discoverSource":37},8401,"amp","amphp\u002Famp","amphp","A non-blocking concurrency framework for PHP applications. 🐘","https:\u002F\u002Famphp.org\u002Famp",null,"PHP",4417,258,122,15,0,1,4,29.24,"MIT License",false,"3.x",[7,24,25,26,27,28,29,30,31],"async","asynchronous","concurrency","coroutines","futures","php","promises","revolt","2026-06-12 02:01:53","# amphp\u002Famp\n\nAMPHP is a collection of event-driven libraries for PHP designed with fibers and concurrency in mind.\n`amphp\u002Famp` specifically provides futures and cancellations as fundamental primitives for asynchronous programming.\nWe're now using [Revolt](https:\u002F\u002Frevolt.run\u002F) instead of shipping an event loop implementation with `amphp\u002Famp`.\n\nAmp makes heavy use of fibers shipped with PHP 8.1 to write asynchronous code just like synchronous, blocking code. In\ncontrast to earlier versions, there's no need for generator based coroutines or callbacks. Similar to threads, each\nfiber has its own call stack, but fibers are scheduled cooperatively by the event loop. Use `Amp\\async()` to run things\nconcurrently.\n\n## Motivation\n\nTraditionally, PHP follows a sequential execution model.\nThe PHP engine executes one line after the other in sequential order.\nOften, however, programs consist of multiple independent sub-programs which can be executed concurrently.\n\nIf you query a database, you send the query and wait for the response from the database server in a blocking manner.\nOnce you have the response, you can start doing the next thing.\nInstead of sitting there and doing nothing while waiting, we could already send the next database query, or do an HTTP call to an API.\nLet's make use of the time we usually spend on waiting for I\u002FO!\n\n[Revolt](https:\u002F\u002Frevolt.run\u002F) allows such concurrent I\u002FO operations. We keep the cognitive load low by avoiding callbacks.\nOur APIs can be used like any other library, except that things _also_ work concurrently, because we use non-blocking I\u002FO under the hood.\nRun things concurrently using `Amp\\async()` and await the result using `Future::await()` where and when you need it!\n\nThere have been various techniques for implementing concurrency in PHP over the years, e.g. callbacks and generators shipped in PHP 5.\nThese approaches suffered from the [\"What color is your function\"](https:\u002F\u002Fjournal.stuffwithstuff.com\u002F2015\u002F02\u002F01\u002Fwhat-color-is-your-function\u002F) problem, which we solved by shipping Fibers with PHP 8.1.\nThey allow for concurrency with multiple independent call stacks.\n\nFibers are cooperatively scheduled by the [event-loop](https:\u002F\u002Frevolt.run), which is why they're also called coroutines.\nIt's important to understand that only one coroutine is running at any given time, all other coroutines are suspended in the meantime.\n\nYou can compare coroutines to a computer running multiple programs using a single CPU core.\nEach program gets a timeslot to execute.\nCoroutines, however, are not preemptive.\nThey don't get their fixed timeslot.\nThey have to voluntarily give up control to the event loop.\n\nAny blocking I\u002FO function blocks the entire process while waiting for I\u002FO.\nYou'll want to avoid them.\nIf you haven't read the installation guide, have a look at the [Hello World example](https:\u002F\u002Fv3.amphp.org\u002Finstallation#hello-world) that demonstrates the effect of blocking functions.\nThe libraries provided by AMPHP avoid blocking for I\u002FO.\n\n## Installation\n\nThis package can be installed as a [Composer](https:\u002F\u002Fgetcomposer.org\u002F) dependency.\n\n```bash\ncomposer require amphp\u002Famp\n```\n\nIf you use this library, it's very likely you want to schedule events using [Revolt](https:\u002F\u002Frevolt.run),\nwhich you should require separately, even if it's automatically installed as a dependency.\n\n```bash\ncomposer require revolt\u002Fevent-loop\n```\n\nThese packages provide the basic building blocks for asynchronous \u002F concurrent applications in PHP. We offer a lot of packages\nbuilding on top of these, e.g.\n\n- [`amphp\u002Fbyte-stream`](https:\u002F\u002Fgithub.com\u002Famphp\u002Fbyte-stream) providing a stream abstraction\n- [`amphp\u002Fsocket`](https:\u002F\u002Fgithub.com\u002Famphp\u002Fsocket) providing a socket layer for UDP and TCP including TLS\n- [`amphp\u002Fparallel`](https:\u002F\u002Fgithub.com\u002Famphp\u002Fparallel) providing parallel processing to utilize multiple CPU cores and\n  offload blocking operations\n- [`amphp\u002Fhttp-client`](https:\u002F\u002Fgithub.com\u002Famphp\u002Fhttp-client) providing an HTTP\u002F1.1 and HTTP\u002F2 client\n- [`amphp\u002Fhttp-server`](https:\u002F\u002Fgithub.com\u002Famphp\u002Fhttp-server) providing an HTTP\u002F1.1 and HTTP\u002F2 application server\n- [`amphp\u002Fmysql`](https:\u002F\u002Fgithub.com\u002Famphp\u002Fmysql) and [`amphp\u002Fpostgres`](https:\u002F\u002Fgithub.com\u002Famphp\u002Fpostgres) for\n  non-blocking database access\n- and [many more packages](https:\u002F\u002Fgithub.com\u002Famphp?type=source)\n\n## Requirements\n\nThis package requires PHP 8.1 or later. No extensions required!\n\n[Extensions](https:\u002F\u002Frevolt.run\u002Fextensions) are only needed if your app necessitates a high numbers of concurrent socket\nconnections, usually this limit is configured up to 1024 file descriptors.\n\n## Usage\n\n### Coroutines\n\nCoroutines are interruptible functions. In PHP, they can be implemented using [fibers](https:\u002F\u002Fwiki.php.net\u002Frfc\u002Ffibers).\n\n> **Note**\n> Previous versions of Amp used generators for a similar purpose, but fibers can be interrupted anywhere in the call stack making previous boilerplate like `Amp\\call()` unnecessary.\n\nAt any given time, only one fiber is running. When a coroutine suspends, execution of the coroutine is temporarily\ninterrupted, allowing other tasks to be run. Execution is resumed once a timer expires, stream operations are possible,\nor any awaited `Future` completes.\n\nLow-level suspension and resumption of coroutines is handled by Revolt's [`Suspension`](https:\u002F\u002Frevolt.run\u002Ffibers) API.\n\n```php\n\u003C?php\n\nuse Revolt\\EventLoop;\n\nrequire __DIR__ . '\u002Fvendor\u002Fautoload.php';\n\n$suspension = EventLoop::getSuspension();\n\nEventLoop::delay(5, function () use ($suspension): void {\n    print '++ Executing callback created by EventLoop::delay()' . PHP_EOL;\n\n    $suspension->resume(null);\n});\n\nprint '++ Suspending to event loop...' . PHP_EOL;\n\n$suspension->suspend();\n\nprint '++ Script end' . PHP_EOL;\n```\n\nCallbacks registered on the Revolt event-loop are automatically run as coroutines. It is safe to suspend within those\ncallbacks. Apart from the event-loop API, `Amp\\async()` can be used to start a coroutine (that is, a new fiber or an\nindependent call stack).\n\n```php\n\u003C?php\n\nrequire __DIR__ . '\u002Fvendor\u002Fautoload.php';\n\nAmp\\async(function () {\n    print '++ Executing callback passed to async()' . PHP_EOL;\n\n    Amp\\delay(3);\n\n    print '++ Finished callback passed to async()' . PHP_EOL;\n});\n\nprint '++ Suspending to event loop...' . PHP_EOL;\nAmp\\delay(5);\n\nprint '++ Script end' . PHP_EOL;\n```\n\n### Future\n\nA `Future` is an object representing the eventual result of an asynchronous operation. Such placeholders are also\ncalled a \"promise\" in other frameworks or languages such as JavaScript. We chose to not use the \"promise\" name since a\n`Future` does not have a `then` method, which is typical of most promise implementations. Futures are primarily designed\nto be awaited in coroutines, though `Future` also has methods which act upon the result, returning another future.\n\nA future may be in one of three states:\n\n- **Completed**: The future has been completed successfully.\n- **Errored**: The future failed with an exception.\n- **Pending**: The future is still pending.\n\nA successfully completed future is analog to a return value, while an errored future is analog to throwing an exception.\n\nOne way to approach asynchronous APIs is using callbacks that are passed when the operation is started and called once it completes:\n\n```php\ndoSomething(function ($error, $value) {\n    if ($error) {\n        \u002F* ... *\u002F\n    } else {\n        \u002F* ... *\u002F\n    }\n});\n```\n\nThe callback approach has several drawbacks.\n\n- Passing callbacks and doing further actions in them that depend on the result of the first action gets messy really\n  quickly.\n- An explicit callback is required as input parameter to the function, and the return value is simply unused. There's\n  no way to use this API without involving a callback.\n\nThat's where futures come into play.\nThey're placeholders for the result that are returned like any other return value.\nThe caller has the choice of awaiting the result using `Future::await()` or registering one or several callbacks.\n\n```php\ntry {\n    $value = doSomething()->await();\n} catch (...) {\n    \u002F* ... *\u002F\n}\n```\n\n```php\npublic function await(): mixed\n```\n\nSuspends the current coroutine until the future is completed or errors. The future result is returned or an exception\nthrown if the future errored.\n\n```php\n\u002F** @param Closure(mixed $value): mixed $map *\u002F\npublic function map(Closure $map): Future\n```\n\nAttaches a callback which is invoked if the future completes successfully, passing the future result as an argument.\nAnother future is returned, which either completes with the return value of the callback, or errors if the callback\nthrows an exception.\n\n```php\n\u002F** @param Closure(Throwable $exception): mixed $catch *\u002F\npublic function catch(Closure $catch): Future\n```\n\nAttaches a callback which is invoked if the future errors, passing the exception as the callback parameter.\nAnother future is returned, which either completes with the return value of the callback, or errors if the callback\nthrows an exception.\n\n```php\n\u002F** @param Closure(): void $finally *\u002F\npublic function finally(Closure $finally): Future\n```\n\nAttaches a callback which is always invoked, whether the future completes or errors.\nAnother future is returned, which either completes with same value as the future, or errors if the callback\nthrows an exception.\n\n#### Combinators\n\nIn concurrent applications, there will be multiple futures, where you might want to await them all or just the first one.\n\n##### await\n\n`Amp\\Future\\await($iterable, $cancellation)` awaits all `Future` objects of an `iterable`. If one of the `Future` instances errors, the operation\nwill be aborted with that exception. Otherwise, the result is an array matching keys from the input `iterable` to their\ncompletion values.\n\nThe `await()` combinator is extremely powerful because it allows you to concurrently execute many asynchronous operations\nat the same time. Let's look at an example using [`amphp\u002Fhttp-client`](https:\u002F\u002Fgithub.com\u002Famphp\u002Fhttp-client) to\nretrieve multiple HTTP resources concurrently:\n\n```php\n\u003C?php\n\nuse Amp\\Future;\nuse Amp\\Http\\Client\\HttpClientBuilder;\nuse Amp\\Http\\Client\\Request;\n\nrequire __DIR__ . '\u002Fvendor\u002Fautoload.php';\n\n$httpClient = HttpClientBuilder::buildDefault();\n$uris = [\n    \"google\" => \"https:\u002F\u002Fwww.google.com\",\n    \"news\"   => \"https:\u002F\u002Fnews.google.com\",\n    \"bing\"   => \"https:\u002F\u002Fwww.bing.com\",\n    \"yahoo\"  => \"https:\u002F\u002Fwww.yahoo.com\",\n];\n\ntry {\n    $responses = Future\\await(array_map(function ($uri) use ($httpClient) {\n        return Amp\\async(fn () => $httpClient->request(new Request($uri, 'HEAD')));\n    }, $uris));\n\n    foreach ($responses as $key => $response) {\n        printf(\n            \"%s | HTTP\u002F%s %d %s\\n\",\n            $key,\n            $response->getProtocolVersion(),\n            $response->getStatus(),\n            $response->getReason()\n        );\n    }\n} catch (Exception $e) {\n    \u002F\u002F If any one of the requests fails the combo will fail\n    echo $e->getMessage(), \"\\n\";\n}\n```\n\n##### awaitAnyN\n\n`Amp\\Future\\awaitAnyN($count, $iterable, $cancellation)` is the same as `await()` except that it tolerates individual errors. A result is returned once\nexactly `$count` instances in the `iterable` complete successfully. The return value is an array of values. The\nindividual keys in the component array are preserved from the `iterable` passed to the function for evaluation.\n\n##### awaitAll\n\n`Amp\\Future\\awaitAll($iterable, $cancellation)` awaits all futures and returns their results as `[$errors, $values]` array.\n\n##### awaitFirst\n\n`Amp\\Future\\awaitFirst($iterable, $cancellation)` unwraps the first completed `Future`, whether successfully completed or errored.\n\n##### awaitAny\n\n`Amp\\Future\\awaitAny($iterable, $cancellation)` unwraps the first successfully completed `Future`.\n\n#### Future Creation\n\nFutures can be created in several ways. Most code will use [`Amp\\async()`](#Coroutines) which takes a function and runs it as coroutine in another Fiber.\n\nSometimes an interface mandates a `Future` to be returned, but results are immediately available, e.g. because they're cached.\nIn these cases `Future::complete(mixed)` and `Future::error(Throwable)` can be used to construct an immediately completed `Future`.\n\n##### DeferredFuture\n\n> **Note**\n> The `DeferredFuture` API described below is an advanced API that many applications probably don't need.\n> Use [`Amp\\async()`](#Coroutines) or [combinators](#Combinators) instead where possible.\n\n`Amp\\DeferredFuture` is responsible for completing a pending `Future`.\nYou create a `Amp\\DeferredFuture` and uses its `getFuture` method to return an `Amp\\Future` to the caller.\nOnce result is ready, you complete the `Future` held by the caller using `complete` or `error` on the linked `DeferredFuture`.\n\n```php\nfinal class DeferredFuture\n{\n    public function getFuture(): Future\n    public function complete(mixed $value = null): void\n    public function error(Throwable $throwable): void\n}\n```\n\n> **Warning**\n> If you're passing `DeferredFuture` objects around, you're probably doing something wrong.\n> They're supposed to be internal state of your operation.\n\n> **Warning**\n> You can't complete a future with another future; Use `Future::await()` before calling `DeferredFuture::complete()` in such cases.\n\nHere's a simple example of an asynchronous value producer `asyncMultiply()` creating a `DeferredFuture` and returning the\nassociated `Future` to its caller.\n\n```php\n\u003C?php \u002F\u002F Example async producer using DeferredFuture\n\nuse Amp\\Future;\nuse Revolt\\EventLoop;\n\nrequire __DIR__ . '\u002Fvendor\u002Fautoload.php';\n\nfunction asyncMultiply(int $x, int $y): Future\n{\n    $deferred = new Amp\\DeferredFuture;\n\n    \u002F\u002F Complete the async result one second from now\n    EventLoop::delay(1, function () use ($deferred, $x, $y) {\n        $deferred->complete($x * $y);\n    });\n\n    return $deferred->getFuture();\n}\n\n$future = asyncMultiply(6, 7);\n$result = $future->await();\n\nvar_dump($result); \u002F\u002F int(42)\n```\n\n### Cancellation\n\nEvery operation that supports cancellation accepts an instance of `Cancellation` as argument.\nCancellations are objects that allow registering handlers to subscribe to cancellation requests.\nThese objects are passed down to sub-operations or have to be handled by the operation itself.\n\n`$cancellation->throwIfRequested()` can be used to fail the current operation with a `CancelledException` once cancellation has been requested.\nWhile `throwIfRequested()` works well, some operations might want to subscribe with a callback instead. They can do so\nusing `Cancellation::subscribe()` to subscribe any cancellation requests that might happen.\n\nThe caller creates a `Cancellation` by using one of the implementations below.\n\n> **Note**\n> 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.\n\n#### TimeoutCancellation\n\nA `TimeoutCancellations` automatically cancels itself after the specified number of seconds.\n\n```php\nrequest(\"...\", new Amp\\TimeoutCancellation(30));\n```\n\n#### SignalCancellation\n\nA `SignalCancellation` automatically cancels itself after a specified signal has been received by the current process.\n\n```php\nrequest(\"...\", new Amp\\SignalCancellation(SIGINT));\n```\n\n#### DeferredCancellation\n\nA `DeferredCancellation` allows manual cancellation with the call of a method.\nThis is the preferred way if you need to register some custom callback somewhere instead of shipping your own implementation.\nOnly the caller has access to the `DeferredCancellation` and can cancel the operation using `DeferredCancellation::cancel()`.\n\n```php\n$deferredCancellation = new Amp\\DeferredCancellation();\n\n\u002F\u002F Register some custom callback somewhere\nonSomeEvent(fn () => $deferredCancellation->cancel());\n\nrequest(\"...\", $deferredCancellation->getCancellation());\n```\n\n#### NullCancellation\n\nA `NullCancellation` will never be cancelled.\nCancellation is often optional, which is usually implemented by making the parameter nullable.\nTo avoid guards like `if ($cancellation)`, a `NullCancellation` can be used instead.\n\n```php\n$cancellation ??= new NullCancellationToken();\n```\n\n#### CompositeCancellation\n\nA `CompositeCancellation` combines multiple independent cancellation objects. If any of these cancellations is cancelled, the `CompositeCancellation` itself will be cancelled.\n\n### Utilities\n\nSeveral utility functions and classes are also included in this library.\n\n```php\nfunction delay(\n    float $timeout,\n    bool $reference = true,\n    ?Cancellation $cancellation = null,\n): void\n```\n\n`delay` suspends the current coroutine (fiber) until the given timeout has elapsed or, if provided, the cancellation\nis cancelled. Optionally, the underlying event-loop callback may be unreferenced, allowing the event-loop to exit\nif no other referenced events are active.\n\n```php\n\u002F** @param int|array\u003Cint> $signals *\u002F\nfunction trapSignal(\n    int|array $signals,\n    bool $reference = true,\n    ?Cancellation $cancellation = null,\n): int\n```\n\n`trapSignal` suspends the current coroutine (fiber) until one of the given signals is received by the process or, if\nprovided, the cancellation is cancelled. Optionally, the underlying event-loop callback may be unreferenced, allowing\nthe event-loop to exit if no other referenced events are active. The signal number of the received signal is returned.\n\n```php\nfunction now(): float\n```\n\n`now` returns a high-resolution time relative to an arbitrary point in time. This function may be used to calculate\ntime differences independent of wall-time.\n\n```php\n\u002F**\n * @template TClosure of \\Closure\n * @param TClosure $closure\n * @return TClosure\n *\u002F\nfunction weakClosure(Closure $closure): Closure\n```\n\n`weakClosure` wraps a given closure, returning a new `Closure` instance which maintains a weak-reference to any\n`$this` object held by the closure (a weak-closure). This allows a class instance to hold a self-referencing closure\nwithout creating a circular-reference that would prevent or delay automatic garbage collection. Invoking the returned\n`Closure` after the object is destroyed will throw an instance of `Error`.\n\n#### Interval\n\nAn `Interval` registers a callback in the event-loop which is invoked within a new coroutine every given number of\nseconds until either the `Interval::disable()` method is called or the object is destroyed. If an `Interval` is\ndisabled, it can be re-enabled using `Interval::enable()`.\n\nHolding an instance of `Interval` within an instance of another class is a convenient way to run a repeating timer\nduring the existence of that object. When the holding object is destroyed, the instance of `Interval` will also be\ndestroyed, cancelling the repeating timer in the event-loop. Use `weakClosure()` to avoid having a circular reference\nto the holding object, which will delay garbage collection of the holding object.\n\n```php\n\u002F\u002F Creates a callback which is invoked every 0.5s\n\u002F\u002F unless disabled or the object is destroyed.\n$interval = new Interval(0.5, function (): void {\n    \u002F\u002F ...\n});\n\n\u002F\u002F Disable the repeating timer, stopping future\n\u002F\u002F invocations until enabled again.\n$interval->disable();\n\n\u002F\u002F Enable the repeating timer. The callback will\n\u002F\u002F not be invoked until the given timeout has elapsed.\n$interval->enable();\n```\n\n## Versioning\n\n`amphp\u002Famp` follows the [semver](http:\u002F\u002Fsemver.org\u002F) semantic versioning specification like all other `amphp` packages.\n\n## Compatible Packages\n\nCompatible packages should use the [`amphp`](https:\u002F\u002Fgithub.com\u002Fsearch?utf8=%E2%9C%93&q=topic%3Aamphp) topic on GitHub.\n\n## Security\n\nIf you discover any security related issues, please email [`me@kelunik.com`](mailto:me@kelunik.com) instead of using the\nissue tracker.\n\n## License\n\nThe MIT License (MIT). Please see [`LICENSE`](.\u002FLICENSE) for more information.\n","amphp\u002Famp 是一个为 PHP 应用程序设计的非阻塞并发框架。它利用 PHP 8.1 引入的 Fibers 提供了 futures 和取消操作等异步编程的基本原语，使开发者能够像编写同步代码一样编写异步代码。通过使用 `Amp\\async()` 函数可以轻松实现并发执行，并且借助 [Revolt](https:\u002F\u002Frevolt.run\u002F) 事件循环来调度这些并发任务。此项目特别适合需要处理大量 I\u002FO 操作（如数据库查询、HTTP 请求）的场景，能够在等待 I\u002FO 完成时继续执行其他任务，从而提高应用程序的整体性能和响应速度。",2,"2026-06-11 03:17:46","top_language"]