前回の記事で、PHPでも非同期処理を同期処理の文法で書くことを実現できました。
これで、ReactPHPを使った非同期処理サーバの構築が捗りますよね!
まあ、そんなわけ無いですよね。。。
肝心の非同期処理がないのですから。
ということで、今回は非同期処理を作るための準備をしてみようと思います。
もっとも、私自身も大した実装ができるわけではないです。
できる人らのコード呼んでもちんぷんかんぷんでした。。。
非同期処理を書こう
非同期処理のインターフェース
そもそも、PHPerで非同期処理を書いたことある人がどれほどいることか。
私も、こういった記事を書くまで、思いつくことすらありませんでした。
非同期処理を書く上で、参考になるのはやはりJavaScriptです。
function asynFunc (args, function () {
//何かの処理
})
非同期処理というのは、基本的には関数の処理が完全に終わる前に、その次の処理が実行されるというもので、完全終了した後にその結果を受ける処理を、コールバックで指示するという書き方が一般的です。
仕組み
非同期処理を実装するためには、どうすればよいでしょうか。
簡単には次のような仕組みを考えると良いと思います。
- 非同期処理が発生したら、あらかじめ「合図」を設定する
- 各「合図」ごとに処理を登録しておく
- 非同期処理の終了を検知したら、その結果に応じて「合図」を出す
- 「合図」を検知したらそれに対応する処理を実行する
という形です。
で、この「合図」をイベントと呼んでいます。
次に、これらの機構を実現するためのイベントとループについて解説します。
イベント
ReactPHPをインストールすると、依存パッケージとして「evenement/evenement」というパッケージが同時にインストールされます。
このパッケージは先程述べたイベントの実装を容易にしてくれます。
実装は次のように行います。
$emitter = new Evenement\EventEmitter();// イベントの仕組みを生成
$emitter->on('go', function ($data) {// 'go'という名前のイベントが発生した時の処理を登録(バインド)
print_r($data);
});
$data = ['woops!']
$emitter->emit('go', [$data]);// 'go'という名前のイベントを発生させる(発火)
on
メソッドで処理を登録し、emit
メソッドでイベントを発生させます。これらをそれぞれ、バインドと発火と呼んだりしてます。
emit
の第2変数に与えた配列の中身がon
でバインドしたコールバック関数の引数になります。
ループ
Reactの中枢はループ機構です。
ループというのは、その名の通りループです。
無限ループが発生し、1サイクルごとに何らかの状態を監視している、というものになります。
Reactは主にタイマーとストリームを監視しているようです。
先ほどのイベントの仕組みと合わせて、非同期処理を用意に実装できるようになります。
タイマー
ループを使った機構で最もわかりやすい非同期処理の例はタイマーだと思います。
例えば次のようなコードは、タイマーとイベント発火を組み合わせて、簡単に非同期処理を作成しています
$loop = React\EventLoop\Factory::create();
function timerFunc($loop)
{
$emitter = new \Evenement\EventEmitter();
$loop->addTimer(1, function () use ($emitter) {
$emitter->emit('hello');
});
$emitter->on('hello', function () {
echo 'おはよう' . PHP_EOL;
});
}
timerFunc($loop);
echo 'こんにちは' . PHP_EOL;
$loop->run();
このように、$loop
にあるaddTimer
メソッドを使用することで、タイマーを設定することが可能です。
addTimer メソッド
Reactのループのタイマー処理には2つあって、一つがこのaddTimer
です。
これは、与えられた秒数後に指定したコールバック関数の処理を走らせるというものです。
引数の取り方が逆ですが、JavaScriptのsetTimeout
と同じようなものです。
addPeriodicTimer メソッド
一方、こちらのメソッドは与えられた周期でコールバック関数を実行するので、JSで言うところのsetInterval
と同じようなものです。
ストリーム
Reactではタイマーだけでなく、ストリームの監視も行えます。
私の認識ではストリームというのは外部リソースに問い合わせる処理全部、というものですが、合ってるのかわかりません。。。
また、内容が複雑なので、今のところ私の理解が追いついていません。
ストリームの処理の実処理をするパッケージも有ります。
これもReactに同梱されています。
use React\Stream\Stream;
$loop = React\EventLoop\Factory::create();
$source1 = new Stream(fopen('test1.txt', 'r'), $loop);
$source2 = new Stream(fopen('test2.txt', 'r'), $loop);
$source2->on('data', function($data) {
echo $data;
});
$source1->on('data', function($data) {
echo $data;
});
$loop->run();
ファイルの読み込みとかならこれでいいのですが、肝心のDBアクセスとかどうなるんだか。。。
まとめ
今回は事前準備として必要な知識をピックアップしました。
ストリームでやるのが効率としてはいいのでしょうが、いかんせん私にはまだ理解できない部分が多いです。
なので、次回にはタイマーを使って非同期処理を実現しようと思います。
参考資料
https://github.com/reactphp/stream
https://github.com/reactphp/event-loop
https://github.com/igorw/evenement