Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
30
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

@ytake

laravel4 websocket push

二回目の微妙な登場でございます。

websocket push!

websocketの代表と言えばchatでしょうか?
laravel4でのチャット実装のそのまんまなサンプルは
laravel4 cookbook
にありますので、
そちらを参考にしていただければと思います。

laravel4パッケージにしたLatchetもありますが、
ratchetを使います。

今回はlaravel4で実装するwebsocket pushについてです

pushをご存知でない方が一番分かりやすいのは、
iPhoneやandroidでニュースやメッセージを受信したときに通知されるnotificationの
web版だと思ってください。

つまり、アクセスしている人にサーバから送信する事ができます。
node.jsで広告やコンテンツを一斉に配信したり、
ブラウザゲーム系にも活用されている方は多いかもしれません。

Ratchetとzmqを使って実装します。(といってもリファレンス通りですが!)
Ratchet
まずサーバpushを実装するにあたっていくつか必要なものがありますので、
makeします。
(mac環境での構築方法なので他のOSの方はそれ用にあわせてください)

libzmq

libzmq
$ git clone git://github.com/zeromq/libzmq.git
$ cd libzmq/
$ ./autogen.sh
$ ./configure && make
$ make install

*errorが出る場合はmacPortsでlibtool autoconf automakeをinstallしてください。

php-zmq

php-zmq
$ git clone git://github.com/mkoppanen/php-zmq.git
$ cd php-zmq/
$ phpize
$ ./configure && make
$ make install

re2cあたりがエラー出る場合は同様にmacPortsでzmq.soをiniに忘れずに追記してください。
php -mで確認してください!

あとはいつもと同じです

composer.json
    "require": {
        "laravel/framework": "4.0.*",
        "cboden/Ratchet": "0.3.*",
        "react/zmq": "0.2.*"
    }

push実装

Wamp/Pusher.php
namespace Wamp;
use Ratchet\ConnectionInterface;
use Ratchet\Wamp\WampServerInterface;

class Pusher implements WampServerInterface
{   
    protected $subscribedTopics = array();

    public function onSubscribe(ConnectionInterface $conn, $topic)
    {
        if (!array_key_exists($topic->getId(), $this->subscribedTopics))
        {
            $this->subscribedTopics[$topic->getId()] = $topic;
        }
    }

    /**
     * @param string JSON'ified string we'll receive from ZeroMQ
     */
    public function onBlogEntry($entry) {
        $entryData = json_decode($entry, true);

        if (!array_key_exists($entryData['cat'], $this->subscribedTopics)) {
            return;
        }

        $topic = $this->subscribedTopics[$entryData['cat']];
        $topic->broadcast($entryData);
    }

    public function onUnSubscribe(ConnectionInterface $conn, $topic) {
    }

    public function onOpen(ConnectionInterface $conn) {
    }

    public function onClose(ConnectionInterface $conn) {
    }

    public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {
        $conn->callError($id, $topic, 'You are not allowed to make calls')->close();
    }

    public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
        $conn->close();
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
    }
}

onBlogEntryが受け、
サーバから接続しているブラウザに送信する様にします。
実際にはDBで配信時間を見ながら自動で送信したりという実装ですが、
書くと長くなるので省きます

コマンド実装

app/commands/WampPushCommand.php

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use React\Socket;

class WampPushCommand extends Command {

    /**
     * @var string
     */
    protected $name = 'wamp:push';

    /**
     * The console command description.
     * @var string
     */
    protected $description = "boot wamp server";

    /**
     * @return void
     */
    public function fire()
    {
        $loop   = React\EventLoop\Factory::create();
        $pusher = new Wamp\Pusher;

        $context = new \React\ZMQ\Context($loop);
        $pull = $context->getSocket(ZMQ::SOCKET_PULL);
        $pull->bind('tcp://127.0.0.1:5555'); 
        $pull->on('message', array($pusher, 'onBlogEntry'));

        $webSock = new \React\Socket\Server($loop);
        $webSock->listen(10000, '0.0.0.0'); 
        $webServer = new Ratchet\Server\IoServer(
            new Ratchet\Http\HttpServer(
                new Ratchet\WebSocket\WsServer(
                    new Ratchet\Wamp\WampServer($pusher
                   )
                )
            ), $webSock);
        $this->info('wamp boot');
        $this->info('Listening on port ' . 10000);
        $loop->run();
    }
}

この例は
port 5555から受け付けて、port 10000にpushを行います。
実際にはコマンドの引数でport指定する様にしましょう。(サンプルなので手抜き)
ここでいうWampとは、The WebSocket Application Messaging Protocolで、
windows環境をさすものではありませんYo

artisanに登録します。

app/start/artisan.php
$artisan->add(new WampPushCommand);

これでwebsocketの準備が整いました。

$ php artisan wamp:push
wamp boot
Listening on port 10000

上記の様に起動します。

次にブラウザです。
折角なのでデフォルトのhello.phpに書いてしまいましょう
リファレンスと同じ様にwhen.js, autobahn.jsにしときます。

hello.phpに書いてみる
<script src="/js/when.js"></script>
<script src="http://autobahn.s3.amazonaws.com/js/autobahn.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<script>
    var conn = new ab.Session(
        'ws://127.0.0.1:10000' , function() {
            conn.subscribe('news', function(topic, data) {
                console.log('New article published to category "' + topic + '" : ' + data.title);
                $('.welcome').html(
                    'New article published to category "' + topic
                    + '"<br /> title : ' + data.title
                    + "<br /> article : " + data.article
                );
            });
        }, function() { 
            console.warn('WebSocket connection closed');
        }, {
            'skipSubprotocolCheck': true
        }
    );
</script>

newsカテゴリーを購読して、受信したときにlaravel4ロゴを書き換えます。(わかりやすい!)
リソースでエントリーします

ResourceController
    public function create()
    {
        $entryData = [
            'cat'     => 'news',
            'title'   => 'ニュース!',
            'article' => 'laravel4.1 でましたよ',
            'when'    => time()
        ];
        $context = new ZMQContext();
        $socket = $context->getSocket(ZMQ::SOCKET_PUSH, 'my pusher1');
        $socket->connect("tcp://localhost:5555");
        $socket->send(json_encode($entryData));
    }

先ほどのlaravel4のデフォルトページをそのまま表示させておき、
他のブラウザかタブからリソースを叩いて送ってみましょう。

sample.png

じゃーん

今回はリソース直叩きなので、普通のwebsocketと変わらないじゃん!と言われそうですが、
実際にはこれをサーバのみで実装します。
webでできるなんて、ちょっと気持ち悪いですね!!!
慣れるまでちょっと長い道のりですが、
実装できる様になると色んなものに使えると思います。
laravel4でwebsocketアプリケーションを実装する事で、
ブラウザからごにょごにょしたデータをnode.jsに送って解析して云々などしなくても、
用意されているものでユーザーデータだったり、色んな情報も簡単に実装する事ができます。
Illuminate万歳!

明日はatijust さんです

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
30
Help us understand the problem. What are the problem?