laravel4 websocket push

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

#websocket push!
websocketの代表と言えばchatでしょうか？
laravel4でのチャット実装のそのまんまなサンプルは
[laravel4 cookbook](https://leanpub.com/laravel4cookbook-jp "laravel4 cookbook")
にありますので、
そちらを参考にしていただければと思います。

laravel4パッケージにした[Latchet](https://github.com/sidneywidmer/Latchet "Latchet")もありますが、
ratchetを使います。


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

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

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

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

####libzmq
```bash: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
```bash: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**で確認してください！

あとはいつもと同じです

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

####push実装

```php: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で配信時間を見ながら自動で送信したりという実装ですが、
書くと長くなるので省きます

####コマンド実装

```php: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に登録します。

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

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

```bash:
$ php artisan wamp:push
wamp boot
Listening on port 10000
```
上記の様に起動します。

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

```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ロゴを書き換えます。(わかりやすい！)
リソースでエントリーします

```php: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](https://qiita-image-store.s3.amazonaws.com/0/32814/92e31b51-df59-4a7e-a424-7bed916e4b2b.png)

####じゃーん

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

明日はatijust さんです