23
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHP非同期処理チュートリアル: ReactPHPで非同期処理を作るための事前準備

Posted at

前回の記事で、PHPでも非同期処理を同期処理の文法で書くことを実現できました。
これで、ReactPHPを使った非同期処理サーバの構築が捗りますよね!

まあ、そんなわけ無いですよね。。。
肝心の非同期処理がないのですから。

ということで、今回は非同期処理を作るための準備をしてみようと思います。
もっとも、私自身も大した実装ができるわけではないです。
できる人らのコード呼んでもちんぷんかんぷんでした。。。

非同期処理を書こう

非同期処理のインターフェース

そもそも、PHPerで非同期処理を書いたことある人がどれほどいることか。
私も、こういった記事を書くまで、思いつくことすらありませんでした。

非同期処理を書く上で、参考になるのはやはりJavaScriptです。

function asynFunc (args, function () {
  //何かの処理
})

非同期処理というのは、基本的には関数の処理が完全に終わる前に、その次の処理が実行されるというもので、完全終了した後にその結果を受ける処理を、コールバックで指示するという書き方が一般的です。

仕組み

非同期処理を実装するためには、どうすればよいでしょうか。
簡単には次のような仕組みを考えると良いと思います。

  1. 非同期処理が発生したら、あらかじめ「合図」を設定する
  2. 各「合図」ごとに処理を登録しておく
  3. 非同期処理の終了を検知したら、その結果に応じて「合図」を出す
  4. 「合図」を検知したらそれに対応する処理を実行する

という形です。
で、この「合図」をイベントと呼んでいます。

次に、これらの機構を実現するためのイベントとループについて解説します。

イベント

ReactPHPをインストールすると、依存パッケージとして「evenement/evenement」というパッケージが同時にインストールされます。
このパッケージは先程述べたイベントの実装を容易にしてくれます。

実装は次のように行います。

event.php
$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

23
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?