PHPでWebSocket


PHPでWebSocketやっていきます

# プロジェクト作成
Composerプロジェクトをつくります
Composerをインストールしていない方はインストールしてください
[Composerをインストールする](https://qiita.com/toontoon/items/62a42ca2d05735a8c082)

適当なフォルダをつくって
コマンドラインでcdして行きます
コマンドラインで
`composer init`
これでcomposer.jsonできます
今回はRatchetを使っていきます
[Ratchet (http://socketo.me/)](http://socketo.me/)
コマンドラインで
`composer require cboden/ratchet`
これでRatchetが使えるようになります

srcフォルダをつくります

composer.jsonのautoload修正します
composer.jsonは下記にします

```composer.json
{
	"autoload" : {
		"psr-4" : {
			"App\\" : "src"
		}
	},
    "require": {
		"cboden/ratchet": "^0.4.3"
	}
}
```
コマンドラインで
`composer dump-autoload`
これで修正したautoloadが反映されます

フォルダ構成は下記になります

```
`-- sample
    |-- src
    |-- vendor
    `-- composer.json
```

# ファイル作成
(1) srcフォルダ配下にChat.php作成

```Chat.php
<?php
namespace App;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class Chat implements MessageComponentInterface {

    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        foreach ($this->clients as $client) {
            $data = ['msg' => $msg];
            if ($from === $client) {
                $data['position'] = 'right';
            } else {
                $data['position'] = 'left';
            }
            $client->send(json_encode($data));
        }
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        $conn->close();
    }
}
```

webSocket接続してきたら自動的にonOpenが実行されます。今回は`$this->clients`にコネクションを追加しています
クライアントがデータをsendしてきたら自動的にonMessageが実行されます。今回はjsonを返すようにしています

(2) srcフォルダ配下にwsServer.php作成

```wsServer.php
<?php
namespace App;

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use App\Chat;

require dirname(__DIR__) . '\vendor\autoload.php';

$server = IoServer::factory(
    new HttpServer(
        new WsServer(
            new Chat()
            )
        ),
    8282
    );

$server->run();
```
webSocketのエントリポイントになります
8282は今回WebSocketで使うポートです。firewallで許可しているポートにしてください
先ほど作成したChatクラスを与えてWebSocketサーバーをrunします

(3) HTTPサーバーの公開フォルダにws.html作成

```ws.html
<html>
<head>
<title>sample</title>
<style type="text/css">
.container {
    width: 100%;
    height: 100%;
    box-sizing: border-box;
}
.msg-log {
    width: 100%;
    height: 92%;
    vertical-align:top;
    box-sizing: border-box;
    padding: 0px;
    border: black solid 1px;
    overflow-y: scroll;
}
.input-area {
    width: 100%;
    box-sizing: border-box;
}
.msg {
    width: 90%;
    height: 8%;
    vertical-align:top;
    box-sizing: border-box;
    padding: 0px;
    float: left;
}
.btn {
    width: 10%;
    height: 8%;
    vertical-align:top;
    box-sizing: border-box;
    padding: 0px;
}
.receive-msg-left {
    border-radius: 10px;
    border: black solid 1px;
    padding: 10px;
    margin: 10px;
    display: inline-block;
    float: left;
}
.receive-msg-right {
    border-radius: 10px;
    border: black solid 1px;
    padding: 10px;
    margin: 10px;
    display: inline-block;
    float: right;
    background-color: #00FF00;
}
.br {
    clear: both;
}
</style>
<script type="text/javascript">

  var conn = "";

  function open(){

      conn = new WebSocket('ws://localhost:8282');

      conn.onopen = function(e) {
      };

      conn.onerror = function(e) {
        alert("エラーが発生しました");
      };

      conn.onmessage = function(e) {
          var data = JSON.parse(e.data);
          var divObj = document.createElement("DIV");
          if (data["position"] == "left") {
              divObj.className = 'receive-msg-left';
          } else {
              divObj.className = 'receive-msg-right';
          }
          var msgSplit = data["msg"].split('\n');
          for (var i in msgSplit) {
              var msg = document.createTextNode(msgSplit[i]);
              var rowObj = document.createElement("DIV");
              rowObj.appendChild(msg);
              divObj.appendChild(rowObj);
          }

          var msgLog = document.getElementById("msg_log");
          msgLog.appendChild(divObj);

          var br = document.createElement("BR");
          br.className = 'br';
          msgLog.appendChild(br);

          msgLog.scrollTop = msgLog.scrollHeight;

      };

      conn.onclose = function() {
          alert("切断しました");
          setTimeout(open, 5000);
      };

  }

  function send(){
      conn.send(document.getElementById("msg").value);
  }

  function close(){
      conn.close();
  }

  open();

</script>
</head>
<body>
    <div class="container">
        <div id="msg_log" class="msg-log"></div>
        <div class="input-area">
            <textarea id="msg" class="msg"></textarea>
            <button class="btn" onclick="send();" >送信</button>
        </div>
    </div>
</body>
</html>
```

`conn = new WebSocket('ws://localhost:8282');`で接続してます。8282は先ほどwsServer.phpに書いたポートです
`conn.send(document.getElementById("msg").value);`でwebSocketサーバーにデータ送信しています
`conn.onmessage`はwebSocketサーバーからsendがあった場合に自動的に実行されます

# 動作確認
コマンドラインで
srcフォルダにcdして
`php wsServer.php`
これでWebSocketサーバーが起動しました

HTTPサーバを起動し
HTTPサーバーの公開フォルダにつくったws.htmlにブラウザでアクセスしてみましょう
`http://localhost/ws.html`
ChromeとFirefoxでアクセスしてみましょう

Chromeでテストクロームと入力し、送信ボタンをクリックします
Firefoxでテストファイヤーフォックスと入力し、送信ボタンをクリックします

動きました

Chromeの画面
![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238103/3f6b1262-5791-c807-c98a-b9e10cd4e1fa.png)

Firefoxの画面
![b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238103/d90e4db9-f933-246e-16c8-8b409db35359.png)

# RatchetをLaravelで使う
LaravelでRatchetを使ってWebSocketする方法は下記記事に書きました
[LaravelでWebSocket](https://qiita.com/toontoon/items/5357460760607a3d432f)