みなさんこんにちは
非同期処理やろうって言うと、やはりjavascriptとかgoとかjavaとかでやって、PHPでやるなんてどうかしてるぜ!とか言われるわけですが。。。
PHPで実装できるということ自体に意味があるのです
目的の設定は、「非同期処理で何かをやる」ではなく「PHPで非同期処理をやる」なのです
そう考えていたら、PHPの非同期処理ライブラリのまとめサイトがあるじゃないですか!
というわけで、サラリと読んでみました
Asynchronous PHP
肝心のまとめサイトはこれです
Asynchronous PHP
2ヶ月くらい前に一時期Reactを調べたりしていたのですが、いやいや、世界は広かった。。。
ライブラリ紹介の前に、非同期の概念について参考となるところを紹介してくれているので、それらを読み返してみるのも一興でしょう
それはさておき、ライブラリ群を早速見ていきましょう
検証環境は docker のphp7.0.4 の公式コンテナです
PHP 非同期処理ライブラリ
amphp
Asyncronous multi tasking PHP の略みたいです(公式)
更新頻度が早いので、未だ活発に開発が続けられているようです
とりあえずインストール
composer require amphp/amp
からの
<?php
require_once '../vendor/autoload.php';
function tick()
{
echo "tick\n";
}
echo "before run()\n";
Amp\run(function() {
Amp\repeat("tick", $msInterval = 1000);
Amp\repeat(function () {
echo "tack\n";
}, 500);
Amp\once("Amp\stop", $msDelay = 5000);
});
echo "after run()\n";
で、
php tick.php
before run()
tack
tick
tack
tack
tick
tack
tack
tick
tack
tack
tick
tack
tack
after run()
こんな感じです。
Web サーバ
Webサーバもあるようなのでブログを参考にいれてみる
composer require amphp/aerys
で
<?php
const AERYS_OPTIONS = [];
$router = Aerys\router()->get("/", function (Aerys\Request $req, Aerys\Response $res) {
$res->send("<h1>Hello World!</h1>");
});
(new Aerys\Host)
->expose("*", 1337)
->use($router);
とやって、../vendor/bin/aerys -d -c wserver.php
でサーバ起動
vendor/amphp/aerys/demo.php に書き方の例が乗っているから、本格的に始めたい場合も便利ですな
websocketも対応していて、割としっかりしている印象です。
ちなみに、PHP7オンリー!
AsyncPHP
これも非同期ライブラリです
早速使ってみましょう
doorman
composer require asyncphp/doorman
でインストールできるので、とりあえずシンプルなサンプルを試してみましょう
<?php
require '../vendor/autoload.php';
use AsyncPHP\Doorman\Manager\ProcessManager;
use AsyncPHP\Doorman\Task\ProcessCallbackTask;
$manager = new ProcessManager();
$task1 = new ProcessCallbackTask(function () {
echo "in task 1\n";
sleep(3);
echo "way!!";
});
$task2 = new ProcessCallbackTask(function () {
echo "in task 2\n";
sleep(5);
echo "End!";
});
$manager->addTask($task1);
$manager->addTask($task2);
while ($manager->tick()) {
usleep(250);
}
これをphp simple.php
で動かそうとしても。。。何も表示されません。
クラス名を見てわかるように、これ、別プロセスに処理を投げているのです。
別ターミナルでtopコマンドを使っていると、突如としてphpプロセスが3つ出てきます(起動したプロセス+非同期タスク2つ)
なので、標準出力に出そうとしても、出てくれないんですな
各プロセスの標準出力をファイルに出すことで、これをある程度解決することができます
$manager = new ProcessManager();
+ $manager->setLogPath(".");
$task1 = new ProcessCallbackTask(function () {
こうすると、その場にstdout.logとstderr.logが生成され、各プロセスの標準出力を確かめることができます。
どちらかと言うと、amphpがイベント式の非同期処理のように見えたけど、AsyncPHPはガッツリマルチプロセス式の非同期処理のように思います。
ついにPHPでもマルチプロセス処理が簡単に書けるライブラリが登場した(してた)ということですかね
pcntlとか、使わんでも、タスクの登録だけで行けそうなのはなかなか興味深いです
Icicle
今まで見たことなかったけど、これ結構スゴイんじゃないか、ってライブラリです。
公式サイト
現在も活発に開発が進んでいるようですので、注目しておいて損はなさそうです
インストール
composer require icicleio/icicle
これでオッケーですが、pcntl および ev エクステンションを導入しておくことをおすすめされます
ただ、今回は検証目的なので、入れません
では、サンプルを
<?php
require '../vendor/autoload.php';
use Icicle\Awaitable;
use Icicle\Coroutine\Coroutine;
use Icicle\Loop;
$generator = function () {
try {
// 現在の時刻を2秒後に$startに渡す
$start = yield Awaitable\resolve(microtime(true))->delay(2);
echo "Sleep time: ", microtime(true) - $start, "\n";
// 処理がリジェクトされた時、例外投げる
yield Awaitable\reject(new Exception('Rejected promise'));
} catch (Exception $e) { // Catches promise rejection reason.
echo "Caught exception: ", $e->getMessage(), "\n";
}
yield Awaitable\resolve('Coroutine completed');
};
$coroutine = new Coroutine($generator());
// コルーチンの実行後の処理(返却値への対応)
$coroutine->done(function ($data) {
echo $data, "\n";
});
Loop\run();
いやぁなるほど〜
Coroutinを使うことで、非同期処理を同期処理の要領で書けるわけですな!
後3ヶ月早く知っておけばよかった。。。
Webサーバもあるようですが、試してみたところ動かなかったので、保留ですかね
React
やったことあるので省略
PHPはReactで非同期処理対応のWEBサーバを構築する
Recoil
@mpyw さんが紹介してくれているので、ここではそこへのリンクを貼ってサボ。。。紙面を節約しましょう
ReactPHP?時代はRecoilPHPだ!
swoole
中国製のフレームワーク
とりあえずご紹介のみ
https://github.com/swoole
まとめ
PHPによる非同期処理のまとめサイトを漁ってみましたが、いやはやこんなにあるとは。
イベントドリブンによる非同期処理のみならず、マルチプロセスを用いたものまで有り、PHPの持つ可能性。。。と言うよりはPHPへのなみなみならぬ情熱を感じずに入られません
個人的にはイベントドリブンをamphpにやらせ、マルチプロセスの必要が出てきたらAsyncPHPにやらせるなどの分担をしちゃってもいいかなって思います
特にamphpはwebサーバやmysqlの非同期処理ライブラリまで実装済みなので、PHPのできることをより押し広げてくれうように思います
最近はperlやったりjavascriptやったり、rubyやったりpythonやったりですが、それでも私はPHPを続けるよ!