PHPは1つのリクエストを上から順に実行する同期処理が基本で、外部APIやデータベースの応答を待つ間、プロセスは何もせず止まっています。PHPの非同期処理は、この待ち時間を重ねて複数のI/Oを同時に進める手法です。拡張を使わないライブラリは2012年公開のReactPHPなど以前からありましたが、PHP 8.1でFiberが標準に入り、コールバックを重ねずに同期処理に近い書き方で扱える土台ができました。
本記事のコードと実行時間は、2026年10月27日にPHP 8.5.8と、2026年11月19日に正式版が出る予定のPHP 8.6.0RC4(公式配布のソースからビルド)で実際に動かして取得したものです。1秒待ってから応答する検証用サーバーに3本のHTTPリクエストを送り、方式ごとの所要時間を比べています。非同期処理という考え方そのものを言語横断で確認したい場合は、非同期処理とは?同期処理との違いから実装方式まで実装者目線で解説を先に読むと以降の説明が追いやすくなります。
検証用サーバーは次のPython 3のスクリプトです。python3 slow_server.pyで起動し、別のターミナルから各PHPの例を実行します。接続ごとに別スレッドで処理し、sで指定した秒数だけ待ってから、idを含むJSONを返します。
import json, os, threading, time
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
class H(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def do_GET(self):
q = parse_qs(urlparse(self.path).query)
s = float(q.get("s", ["1"])[0]); time.sleep(s)
b = json.dumps({"id": q.get("id", [None])[0], "slept": s, "thread": threading.get_ident()}).encode()
self.send_response(200); self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(b))); self.end_headers(); self.wfile.write(b)
def log_message(self, *a): pass
ThreadingHTTPServer(("127.0.0.1", 18711), H).serve_forever()
まとめ:PHPの非同期処理の選び方
- Fiber単体では速くなりません。中断・再開の仕組みだけでイベントループを含まず、Fiberの中で
file_get_contents()を3本呼ぶと逐次と同じ約3.3秒かかりました。 - HTTPを並行に投げたいだけなら、Guzzleの
getAsync()かcurl_multiで足ります。3本で約1.1秒に縮み、イベントループのライブラリを入れる必要もありません。 - HTTP以外のI/Oも並行にするならAMPHP v3。同期コードと同じ書き方で並行に動きますが、
sleep()や同期のPDOを混ぜると全体が止まります。 - ReactPHPはPromise中心の老舗で、
react/asyncを足せばAMPHPと同じくFiberでawait()が書けます。常駐する高負荷サーバーならC++拡張のSwoole、LaravelならキューかConcurrencyファサードが先に検討する候補です。 - PHP 8.6(2026年11月19日予定)で、OSのepoll・kqueueを使う
Io\Pollが本体に入ります。一方、コルーチンとawait関数などを本体に追加するTrue Async RFCは取り下げられており、2026年10月時点でPHPにasync/await構文はありません。
PHPで非同期処理が効く場面と前提
非同期処理が効くI/O待ち中心の処理
非同期処理で短くなるのは、CPUが計算している時間ではなく、相手の応答を待っている時間です。複数の外部APIを呼んで結果をまとめる処理、複数のエンドポイントへ問い合わせるバッチ、WebSocketのように多数の接続を開いたまま捌く常駐サーバーが該当します。画像の変換や暗号化のようなCPU処理は、1つのスレッドで待ちを重ねても速くなりません。CPU処理を同時に進めるには、プロセスかスレッドを増やす並列処理(後述のparallel拡張やpcntl_fork)が必要です。
本記事で扱うFiber・AMPHP・ReactPHPは、1つのOSスレッドの中で処理を切り替える「並行」で、複数のCPUで同時に動かす「並列」ではありません。
PHP-FPMのワーカーとリクエスト内のI/O待ち
一般的なWeb構成では、PHP-FPMのワーカープロセスが1回に1リクエストを処理し、リクエストが終わるたびに変数などの実行状態を破棄します。ワーカー数を増やせば同時に捌けるリクエストは増えますが、1リクエストの中の待ちは短くなりません。1リクエストで外部APIを10本順に呼ぶ処理は、10本分の待ちがそのまま応答時間に足されます。
PHP 8.1のFiberは、この「1リクエスト内の待ち」を重ねるための土台として追加されました。Fibers RFCは、イベントループとasync/awaitを本体に入れることを見送り、最小限の部品だけを提案すると明記しています。イベントループは今もライブラリ側の担当です。
方式の比較:PHPで非同期・並行を実現する7つの手段
2026年10月27日時点の版と、検証用サーバーへの3本のHTTPリクエストにかかった時間(5回計測の範囲)をまとめます。版はPackagist・PECL・GitHubのリリースから取得しました。
| 方式 | 導入 | 2026-10の版 | 対応PHP | 3本の所要時間 | 向く処理 |
|---|---|---|---|---|---|
| 逐次(比較用) | 不要 | – | – | 3.29〜3.45秒 | – |
| Fiber単体 | 標準 | PHP 8.1以降 | 8.1以降 | 3.01〜3.45秒 | ライブラリの部品 |
| Guzzle getAsync | Composer | 8.3.1 | 7.4以降 | 1.02〜1.21秒 | HTTPの並行 |
| AMPHP v3 | Composer | amp 3.1.3 | 8.1以降 | 1.08〜1.37秒 | I/O全般の並行 |
| ReactPHP | Composer | async 4.3.0 | 8.1以降 | 1.02〜1.20秒 | I/O全般・常駐 |
| Swoole | PECL拡張 | 6.2.4 | 8.2〜8.5 | 未計測 | 常駐サーバー |
| Io\Poll | 標準 | PHP 8.6 | 8.6以降 | 1.05〜1.17秒 | ライブラリの部品 |
Guzzle・AMPHP・ReactPHP・Io\Pollの差は0.2秒程度で、計測のぶれの範囲でした。今回のように独立したリクエストを相手が同時に処理できる条件では、どれを選んでも待ち時間を最も遅い1本の分に近づけられます。選ぶ基準は速さではなく、HTTP以外のI/Oも並行にしたいか、既存のコードに組み込めるか、常駐プロセスを運用できるかです。計測はmacOS(Intel Core i9-9880H)上で、検証用サーバーは1リクエストずつ別スレッドで1秒待ってから応答します。
Fiberの仕組みと基本コード
Fiberは中断と再開ができる実行単位
Fiberは独自のコールスタックを持つ関数の実行単位で、Fiber::suspend()を呼んだ位置で止まり、呼び出し側がresume()すると続きから再開します。切り替えるのはOSではなくプログラム自身で、この協調型の切り替えは、グリーンスレッドとは?OSスレッドとの違いとN:1モデルの制約を実測で解説で扱っている仕組みと同じ系統です。
<?php
$fiber = new Fiber(function (string $name): string {
echo "start: {$name}\n";
$received = Fiber::suspend('paused'); // ここで中断し、値を呼び出し側へ渡す
echo "resumed with: {$received}\n";
return 'done';
});
$value = $fiber->start('job-1'); // 最初の suspend まで実行される
echo "main got: {$value}\n";
$fiber->resume('hello'); // suspend の戻り値として渡す
echo "return: {$fiber->getReturn()}\n";
PHP 8.5.8とPHP 8.6.0RC4のどちらで実行しても、出力は次のとおり同じでした。
start: job-1
main got: paused
resumed with: hello
return: done
start()は最初のsuspend()まで実行し、suspend()に渡した値を返します。resume()に渡した値は、Fiberの中でsuspend()の戻り値になります。ジェネレーターのyieldとの違いは、ネストした関数呼び出しの奥からでもsuspend()できる点です。PHPマニュアルのFiberの章が説明するとおり、Fiberは自分のコールスタックごと止まるので、中断する関数の戻り値の型をGeneratorに変えたり、呼び出し元を書き換えたりする必要がありません。
Fiber単体では非同期にならない理由
Fiberの中でブロッキングするI/Oを呼ぶと、そのI/Oが終わるまでプロセス全体が止まります。次のコードは3本のFiberでそれぞれfile_get_contents()を呼びますが、所要時間は3.01〜3.45秒で、逐次実行(3.29〜3.45秒)と変わりませんでした。
<?php
// 1秒待ってから応答する検証用サーバーへ3本リクエストする
$start = hrtime(true);
$fibers = [];
foreach ([1, 2, 3] as $id) {
$fibers[] = new Fiber(fn () => file_get_contents("http://127.0.0.1:18711/?id={$id}&s=1"));
}
foreach ($fibers as $fiber) {
$fiber->start(); // file_get_contents が終わるまで戻らない
}
printf("Fiber + file_get_contents: %.2f s\n", (hrtime(true) - $start) / 1e9);
速くするには、ソケットを非ブロッキングにして「読めるようになったら知らせる」仕組み(イベントループ)を用意し、待っている間にFiberをsuspend()して別のFiberへ切り替える必要があります。この部分を引き受けるのがAMPHPやReactPHPです。Fibers RFCのFAQも、file_get_contents()のようなブロッキング関数はプロセス全体を止めるため、非同期I/Oと組み合わせる必要があると説明しています。
AMPHP v3による複数HTTPリクエストの並行送信
AMPHP v3のインストールと動作要件
AMPHP v3は、Fiberを前提に作り直された非同期ライブラリ群です。イベントループは自前で持たず、Revoltを使います。2026年10月27日時点の版はamphp/amp 3.1.3、revolt/event-loop 1.0.9、HTTPクライアントのamphp/http-client 5.3.6で、いずれもPHP 8.1以降が対象です。拡張のコンパイルは不要で、Composerで入ります。
composer require amphp/amp:^3 amphp/http-client:^5
async()とawait()で3本を並行取得するコード
<?php
require __DIR__ . '/vendor/autoload.php';
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use function Amp\async;
use function Amp\Future\await;
$client = HttpClientBuilder::buildDefault();
$start = hrtime(true);
$futures = [];
foreach ([1, 2, 3] as $id) {
// async() は処理を Fiber に載せ、すぐに Future を返す
$futures[$id] = async(function () use ($client, $id): array {
$response = $client->request(new Request("http://127.0.0.1:18711/?id={$id}&s=1"));
return json_decode($response->getBody()->buffer(), true);
});
}
$results = await($futures); // 3本すべての完了を待つ
printf("AMPHP: %.2f s ids=%s\n", (hrtime(true) - $start) / 1e9, implode(',', array_column($results, 'id')));
5回の実行で1.08〜1.37秒でした。$client->request()は応答を待つ間に自分のFiberを止めてイベントループへ制御を返すので、その間に残りの2本が送信されます。then()やコールバックが出てこず、同期処理と同じ順番で読めるのがAMPHP v3の利点です。
sleep()と同期PDOによるイベントループの停止
AMPHPの中でも、ブロッキングする関数を呼べばFiber単体のときと同じことが起きます。Amp\delay(1)を3本並べると1.16秒で終わりましたが、PHP標準のsleep(1)を3本並べると3.44秒かかりました。
<?php
require __DIR__ . '/vendor/autoload.php';
use function Amp\async;
use function Amp\delay;
use function Amp\Future\await;
$start = hrtime(true);
await([async(fn () => delay(1)), async(fn () => delay(1)), async(fn () => delay(1))]);
printf("delay(1) x3: %.2f s\n", (hrtime(true) - $start) / 1e9);
$start = hrtime(true);
await([async(fn () => sleep(1)), async(fn () => sleep(1)), async(fn () => sleep(1))]);
printf("sleep(1) x3: %.2f s\n", (hrtime(true) - $start) / 1e9);
実務で問題になるのはデータベースです。PDOやmysqliの通常の呼び出しはブロッキングなので、AMPHPの中で呼ぶと応答を待つ間に同じイベントループ上の処理がすべて止まります。DBの待ちも並行にするにはamphp/mysqlやamphp/postgresのような非同期ドライバに置き換える必要があり、同期ドライバで動くLaravelのEloquentなどではDBの待ちを重ねられない点が、AMPHPを採用するときの最大の制約です。AMPHPのREADMEも、ブロッキングするI/O関数は待っている間プロセス全体を止めると注意しています。
Revoltのドライバ選択とstream_selectの制約
Revolt 1.0.9は、拡張のext-uv・ext-ev・ext-eventのどれかがあればそれを使い、どれも無ければPHP標準のstream_select()で動くドライバを選びます(DriverFactoryのソースで確認)。拡張なしの環境でRevolt\EventLoop::getDriver()を表示するとStreamSelectDriverでした。stream_select()はOSのselect()を使うため、扱えるファイルディスクリプタの番号にFD_SETSIZEの上限があり、超えるとRevoltは「You have reached the limits of stream_select()」というエラーを出します。数千の接続を開いたままにする常駐サーバーなら、ext-evかext-uvを入れてください。
ReactPHPとAMPHPの違い
ReactPHPは2012年から続くイベント駆動のライブラリで、HTTP・DNS・ソケット・子プロセスなどの部品をPromiseでつなぎます。2026年10月27日時点の版はreact/event-loop 1.6.0、react/http 1.11.1です。PHP 8.1以降ならreact/async 4.3.0がFiberを使ったasync()とawait()を提供し、AMPHPに近い書き方ができます。
composer require react/async:^4 react/http:^1
<?php
require __DIR__ . '/vendor/autoload.php';
use React\Http\Browser;
use function React\Async\async;
use function React\Async\await;
use function React\Promise\all;
$browser = new Browser();
$start = hrtime(true);
$fetch = async(function (int $id) use ($browser): array {
$response = await($browser->get("http://127.0.0.1:18711/?id={$id}&s=1"));
return json_decode((string) $response->getBody(), true);
});
$results = await(all([$fetch(1), $fetch(2), $fetch(3)])); // Promise をまとめて待つ
printf("ReactPHP: %.2f s\n", (hrtime(true) - $start) / 1e9);
所要時間は1.02〜1.20秒で、AMPHPとの差はぶれの範囲でした。違いは速さではなく設計にあります。
| 観点 | AMPHP v3 | ReactPHP |
|---|---|---|
| 基本の書き方 | Fiber前提・同期と同じ見た目 | Promise(then)が基本 |
| Fiberのawait | 標準で組み込み | react/asyncを追加 |
| イベントループ | Revolt | react/event-loop |
| 対応PHP(コア) | 8.1以降 | event-loopは5.3以降 |
| DBドライバ | amphp/mysql・postgres | react/mysql など |
AMPHP v3はawaitが標準で組み込まれ、HTTP・DB・プロセスなどの部品も最初からFiber前提で作られているため、新しく書くなら第一候補です。PHP 8.0以前の環境が残っている、すでにReactPHPの部品で組まれたコードがある、Promiseで処理の流れを明示したい、という場合はReactPHPを選びます。revolt/event-loop-adapter-reactを入れるとReactPHPの部品をRevoltのループの上で動かせるので、両方のライブラリを1つのプロセスで混ぜることもできます。
GuzzleとcURLだけでHTTPを並行化する方法
並行にしたいのが外部APIへのHTTPだけなら、イベントループのライブラリは要りません。GuzzleはcURL拡張があれば非同期のリクエストをcurl_multiで処理するCurlMultiHandlerを選びます(Guzzle 8.3.1のUtils::chooseHandler()で確認)。PHPのcURL拡張が有効であることをphp -mで確かめてから導入します。
composer require guzzlehttp/guzzle:^8
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Promise\Utils;
$client = new Client(['timeout' => 10]);
$start = hrtime(true);
$promises = [];
foreach ([1, 2, 3] as $id) {
$promises[$id] = $client->getAsync("http://127.0.0.1:18711/?id={$id}&s=1");
}
$responses = Utils::unwrap($promises); // 全件の完了を待つ(1件でも失敗すれば例外)
printf("Guzzle getAsync: %.2f s\n", (hrtime(true) - $start) / 1e9);
Guzzle 8.3.1で1.02〜1.21秒でした。Utils::unwrap()は1件でも失敗すると例外を投げるので、一部の失敗を許すならUtils::settle($promises)->wait()で、各リクエストの成否を個別に受け取ります。LaravelのHttp::pool()もこの仕組みの上にあります。Guzzleを使わずにcurl_multi_init()とcurl_multi_exec()を直接書いた場合も1.00〜1.20秒に収まりましたが、再試行やタイムアウトの扱いを自分で書くことになるので、通常はGuzzle経由をすすめます。
Swoole・OpenSwoole:常駐サーバー型の非同期
SwooleはC++で書かれた拡張で、HTTPサーバー・コルーチン・コネクションプールを内蔵します。PHP-FPMのようにリクエストごとにPHPの実行状態を初期化する方式とは異なり、アプリケーションを常駐させたままコルーチンで多数の接続を捌くので、WebSocketやゲームサーバーのような長時間接続に向きます。2026年10月25日に6.2.4が出ています。
導入前に確認したいのは対応PHPの範囲です。Swooleのサポート表では、6.2系の対応はPHP 8.2〜8.5で、PHPのRC版はサポートしないと明記されています。新しいPHPへの対応は1マイナー版遅れるとも書かれているため、PHP 8.6の正式版が出ても、すぐにSwooleで動かせるとは限りません。6.1系(PHP 8.1〜8.4)の通常サポートは2026年10月24日に終わり、2027年4月24日までセキュリティ修正のみです。フォーク版のOpenSwooleは2026年2月28日に26.2.0を出しています。
常駐型では静的プロパティなどがリクエストをまたいで残るため、既存のコードをそのまま載せると前のリクエストの状態が漏れます。LaravelでSwooleを使う方法と状態リークの具体例はLaravel Octaneとは|ワーカー常駐の仕組みと状態リーク・採用条件で、Go製サーバーで同じ常駐モデルを使う方法はFrankenPHPとは?PHP-FPMとの違い・ワーカーモードの仕組みと導入手順【v1.13対応】で扱っています。
レスポンスの後に重い処理を回す:fastcgi_finish_requestとキュー
やりたいことが、ユーザーへ先に応答を返して、メール送信やログ集計を後から行うことなら、Fiberやイベントループは不要です。PHP-FPM環境にはfastcgi_finish_request()があり、呼んだ時点でレスポンスをクライアントへ送り切り、接続を閉じたまま後続の処理を続けます。
ただし、後続の処理が終わるまでFPMのワーカーは使われたままです。多用するとpm.max_childrenまで埋まり、新しいリクエストは空きを待つ列に並びます。待ちがWebサーバーのタイムアウトを超えると、ゲートウェイエラーになります。後続処理の例外もユーザーには見えません。数秒を超える処理や再実行したい処理は、ジョブキューへ渡して別プロセスのワーカーで実行します。Laravelでのジョブの定義とワーカーの常駐、失敗ジョブの扱いはLaravel Queueの使い方|ジョブ定義・worker常駐・失敗ジョブ運用を13.38で実測にまとめています。
Laravelには、互いに依存しない重い処理を子プロセスで同時に実行するConcurrencyファサードもあります。クロージャをシリアライズして隠しArtisanコマンドで別プロセスとして実行する仕組みで、既定のprocessドライバのほか、CLI限定で速いforkドライバ(spatie/forkが必要)があります。CPU処理を同時に進めたいときは、ZTS版のPHPが必要なparallel拡張(PECLで1.2.15)より、こちらのほうが導入の手間がかかりません。
PHP 8.6のPolling APIと、async/await構文の現状
Io\Pollによる3本のHTTP応答の並行待機
2026年11月19日に正式版が出る予定のPHP 8.6には、Polling API(Io\Poll)が入ります。RFCの投票は賛成33・反対1・棄権4で可決され、ステータスはImplementedです。これまで標準関数でソケットを監視するにはstream_select()しかありませんでしたが、Polling APIはLinuxのepoll、macOSやBSDのkqueue、WindowsのWSAPollなどからOSに合うものを自動で選びます。PHP 8.6.0RC4をmacOSでビルドして実行すると、バックエンドはKqueueが選ばれました。
<?php
// PHP 8.6.0RC4 で実行(PHP 8.5 以前には Io\Poll がない)
use Io\Poll\{Context, Event};
use Time\Duration;
$poll = new Context(); // OS に合うバックエンドを自動で選ぶ
echo "backend: {$poll->getBackend()->name}\n";
$start = hrtime(true);
$socks = [];
foreach ([1, 2, 3] as $id) {
// 接続と送信はブロッキングのまま行い、応答の読み取り待ちだけを Io\Poll に任せる
$sock = stream_socket_client('tcp://127.0.0.1:18711', $errno, $errstr, 5);
fwrite($sock, "GET /?id={$id}&s=1 HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n");
stream_set_blocking($sock, false);
$poll->add(new StreamPollHandle($sock), [Event::Read], $id); // 読めるようになったら通知
$socks[$id] = $sock;
}
$deadline = hrtime(true) + 10 * 1_000_000_000; // 全体の期限は10秒
while ($socks !== [] && hrtime(true) < $deadline) {
foreach ($poll->wait(Duration::fromSeconds(1)) as $watcher) {
$id = $watcher->getData();
$chunk = fread($socks[$id], 8192);
if ($chunk === false) {
throw new RuntimeException("read failed: {$id}"); // 読み取り失敗
}
if (feof($socks[$id])) { // 相手が接続を閉じた
$watcher->remove();
fclose($socks[$id]);
unset($socks[$id]);
}
}
}
printf("Io\\Poll x3: %.2f s (unfinished=%d)\n", (hrtime(true) - $start) / 1e9, count($socks));
出力はbackend: KqueueとIo\Poll x3: 1.05 s (unfinished=0)で、5回の範囲は1.05〜1.17秒でした。このコードで重ねているのは応答の読み取り待ちだけで、接続と送信はブロッキングのままです。拡張なしで待ちを重ねられますが、Polling APIはイベントループではなく「どのソケットが読めるか」を知らせる部品で、タイマーやFiberの切り替え、HTTPの解釈は自分で書くことになります。主な使い手はライブラリで、RevoltやReactPHPが内部で採用すれば拡張なしでもepollで動くようになります。2026年10月27日時点のRevolt 1.0.9のドライバには、まだ入っていません。
Context::wait()の引数型:RFC本文とRC4の差
Polling APIのRFC本文では、Context::wait()の第1引数は秒数の整数です。ところがPHP 8.6.0RC4で$poll->wait(5)と書くと、TypeError: Io\Poll\Context::wait(): Argument #1 ($timeout) must be of type ?Time\Duration, int givenで止まりました。リフレクションで確かめると、RC4のシグネチャはwait(?Time\Duration $timeout = null, ?int $maxEvents = null)です。同じPHP 8.6で追加されたTime\Durationクラスに合わせて変わったもので、上のコードのようにDuration::fromSeconds(5)で渡します。RFCのコード例を写すと動かないので、PHP 8.6のコードはリリースに同梱されるUPGRADINGと実際の挙動で確かめてください。
True Async RFCは取り下げ、エンジン側の新しいRFCは議論中
PHP本体でコルーチンを扱えるようにする提案は、Edmond氏のTrue Async RFCが中心でした。async/awaitのキーワードではなく、Async\spawn()やAsync\await()などの関数とクラスを追加する案です。2025年12月25日付のバージョン1.7では投票期限が2026年2月16日に設定されていましたが、ステータスはCanceledになり、PHP 8.6には入っていません。同じ作者は2026年7月23日に、構文やクラスを追加せず、エンジンにコルーチンとスケジューラを差し込む口だけを作るConcurrency Support in the PHP Engineを出しており、2026年10月時点ではUnder Discussionです。当面、PHPでawaitを書くならAMPHPのAmp\Future\await()かReactPHPのReact\Async\await()という関数を使うことになります。
非同期化を避けるべき場面
次の条件に当てはまるなら、非同期ライブラリは入れないでください。
- 外部I/Oが前の結果に依存して順に呼ぶしかない処理:重ねられる待ちがありません。同時アクセスでワーカーが足りないのが原因なら、PHP-FPMのワーカー数を見直します。
- データベースへの問い合わせが中心の処理:同期ドライバのままではDBの待ちを並行にできず、非同期ドライバへの置き換えはORMの書き直しを伴います。まずSQLとインデックスを見直します。
- CPU処理が重いバッチ:待ちを重ねても速くなりません。キューのワーカーを増やすか、Laravelの
Concurrencyで子プロセスに分けます。 - チームに常駐プロセスの運用経験がないSwoole導入:状態リーク・メモリの増加・再起動の仕組みを用意できないうちは、PHP-FPMのまま外部APIだけをGuzzleで並行にするほうが安全です。
反対に、互いに依存しない外部APIを1リクエストで何本も呼んでいて、応答時間の大半が待ちである場合は、Guzzleの並行リクエストへの書き換えが最初の一手です。相手のレート制限や同時接続数の上限を超えない範囲で本数を決めてください。PHPのバージョンごとのサポート期限はPHPのサポート期限一覧:8.2は2026年末で終了、版ごとのEOLと移行手順【2026年10月】で、AMPHP v3やreact/asyncが前提とするPHP 8.1以降への移行計画と合わせて確認してください。外部APIを多数束ねる連携システムや、常駐プロセスを含む構成の設計は、フルスクラッチ開発として要件の整理から対応しています。
よくある質問
PHPにasync/awaitはありますか?
PHP 8.5にもPHP 8.6.0RC4にも、言語構文としてのasync/awaitはありません。キーワードではなくAsync\spawn()やAsync\await()などの関数・クラスを本体に足すTrue Async RFCも、Canceledになっています。AMPHP v3のAmp\Future\await()か、react/asyncのawait()という関数で同じ書き方をします。
PHPのFiberとは何ですか?
PHP 8.1で追加された、Fiber::suspend()で止まりresume()で続きから動く実行単位です。イベントループを含まないので、単体では速くならず、AMPHPやReactPHPと組み合わせて使います。
ReactPHPとAMPHPはどちらを使えばよいですか?
PHP 8.1以降で新しく書くならAMPHP v3、PHP 8.0以前の環境がある、既存コードがReactPHPで組まれている、Promiseで流れを明示したい場合はReactPHPです。HTTP3本の並行取得では速さの差はありませんでした。
LaravelでPHPの非同期処理をするにはどうしますか?
応答後に回してよい処理はキュー(ジョブとワーカー)、外部APIの同時呼び出しはHttp::pool()、互いに依存しない重い処理を同時に動かすならConcurrency::run()を使います。AMPHPはEloquentなどのブロッキングするDB呼び出しと組み合わせにくいので、Laravelの中では優先度が下がります。
PHPの非同期処理は並列処理ですか?
Fiber・AMPHP・ReactPHPは、1つのOSスレッドの中で処理を切り替える並行処理です。CPU処理を同時に進めるには、parallel拡張(ZTS版PHPが必要)、pcntl_fork、LaravelのConcurrencyのようにスレッドかプロセスを増やします。