ソケット通信サーバーアプリのフレームワークをLaravelと連携してみました

# はじめに

ここでは以下の記事でご紹介しているようなsoket-managerフレームワーク上のプロジェクトをLaravelへ連携する方法をご紹介します。

https://qiita.com/glasses_seven/items/7a490dc8694348505a53/

https://qiita.com/glasses_seven/items/fa9226356815e69e4076/

https://qiita.com/glasses_seven/items/d712808b985aa5253d05/

以降では新規プロジェクトを連携する場合と既成プロジェクトを連携する場合に分けて話を進めます。
それぞれの作業の内訳は以下の通りです。


### 【新規プロジェクトを連携する場合】

<dl>
    <dt>ステップ１）ライブラリをインストール</dt>
    <dt>ステップ２）workerコマンドをコピー</dt>
</dl>


### 【既成プロジェクトを連携する場合】

さらに以下２ステップが必要です。
<dl>
    <dt>ステップ３）既成クラスをコピー</dt>
    <dt>ステップ４）既成のメインクラスをLaravelのCommandクラスへ変換</dt>
    <dt>ステップ５）設定系ファイルのコピー<font color="red">【任意】</font></dt>
</dl>


# 環境

<dl>
    <dt>言語</dt>
    <dd>PHP v8.2.4（v8.1.0以降）</dd>
    <dt>Laravel</dt>
    <dd>v10.48.10</dd>
</dl>


# 新規プロジェクトを連携


### ステップ１）ライブラリをインストール

Laravelのプロジェクトルートで以下のコマンドを実行すればインストールできます。

```terminal:socket-managerライブラリをインストールするコマンド
> composer require socket-manager/library
```


### ステップ２）workerコマンドをコピー

`worker`コマンドをLaravelのプロジェクトルートへコピーします。
コマンドでコピーする場合はLaravelのプロジェクトルートへ移動してから以下のコマンドを実行してください。

```terminal:workerコマンドをコピーするコマンド
> cp ./vendor/socket-manager/library/bin/worker .
```

コピーが完了すると`worker`コマンドはLaravelの`artisan`コマンドと自動で連携します。
Usageを確認すると以下のように表示されます。

```terminal:
> php worker

SOCKET-MANAGER Framework 1.0.0
Usage:
  command [arguments]

 craft
  craft:init <初期化クラス名>                     初期化クラスの生成
  craft:parameter <UNITパラメータクラス名>        UNITパラメータクラスの生成
  craft:protocol <プロトコルUNIT定義のクラス名>   プロトコルUNIT定義のクラスとステータス名Enumの生成
  craft:command <コマンドUNIT定義のクラス名>      コマンドUNIT定義のクラスとキュー／ステータス名Enumの生成
  craft:main <メイン処理のクラス名>               メイン処理クラスの生成
 laravel
  laravel:command <メイン処理のクラス名>          Laravelコマンドクラスの生成

--------------------------------------------------------------------------------
Laravel Framework 10.48.10

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display help for the given command. When no command is given display help for the list command
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  about         

．．．長いので以下省略
```

:::note info
`craft:setting`、`craft:locale`コマンドに関してはLaravel環境では表示されません。
:::

Usageのハイフン線の上側で`worker`コマンドのUsageが、下側で`artisan`コマンドのUsageが表示されていれば連携は完了です。

あとは`worker`コマンドで随時必要なクラスを生成していく事になります。
但し、メインクラスの作成に関してはLaravelのバッチ処理として登録する必要があるので以下のコマンド実行が必要です。

```terminal:コマンドクラス名をMainForTestにする場合
> php worker laravel:command MainForTest

[success] メイン処理クラスの生成に成功しました (MainForTest)
[success] Laravelコマンドクラスの生成に成功しました (MainForTest)
```

このコマンドを実行すると`app/MainClass/MainForTest.php`と`app/Console/Commands/MainForTest.php`が作成されます。
その後Usageを表示するとLaravelのUsage側で以下のように表示されます。

```terminal:Usage表示
> php worker

SOCKET-MANAGER Framework 1.0.0
.
.
.
--------------------------------------------------------------------------------
Laravel Framework 10.48.10
.
.
.
 app
  app:main-for-test       Command description
.
.
.
```

サーバー起動はいつも通りの以下コマンドで実行できます。

```terminal:サーバー起動（10000ポートで起動する場合）
> php worker app:main-for-test 10000
```


# 既成プロジェクトを連携


### ステップ３）既成クラスをコピー

`app`配下の既成クラスはLaravelの`app`配下にディレクトリごとコピーしてください。
`logs`ディレクトリはLaravelのプロジェクトルートにコピーする必要があります。


### ステップ４）既成のメインクラスをLaravelのCommandクラスへ変換

既成のメインクラスはそのままでは使えないので以下のコマンドを実行してLaravelのコマンドクラスへ変換できます。

```terminal:デモ版のWebsocketサーバーを変換する場合
> php worker laravel:command ChatServerForWebsocket

[success] Laravelコマンドクラスの生成に成功しました (ChatServerForWebsocket)
```

この時`app/MainClass`内のファイルはそのまま残ります。
その後Usageを表示するとLaravelのUsage側で以下のように表示されます。

```terminal:Usage表示
> php worker

SOCKET-MANAGER Framework 1.0.0
.
.
.
--------------------------------------------------------------------------------
Laravel Framework 10.48.10
.
.
.
 app
  app:chat-server         チャットサーバー
.
.
.
```

サーバー起動はいつも通りの以下コマンドで実行できます。

```terminal:サーバー起動（10000ポートで起動する場合）
> php worker app:chat-server 10000
```

### ステップ５）設定系ファイルのコピー<font color="red">【任意】</font>

定数やメッセージなどの設定系のファイルを利用されている場合は適宜Laravel環境へコピーしてください。
設定値を取得するヘルパー関数の仕様はLaravelに合わせてあるのでソースを変更する必要はありません。

対応するディレクトリの名前は次の通りです。
<dl>
    <dt>・setting(socket-manager用) ⇒ config(laravel用)</dt>
    <dt>・locale(socket-manager用) ⇒ lang(laravel用)</dt>
</dl>

※ディレクトリごとファイルを上書きしてしまう事故を防ぐためにディレクトリ名は敢えて異なるものを採用しています。
※重複しているファイル名がある場合は設定内容をご自身の目で確認しながらマージを行ってください。
※`setting/app.php`ファイルにはタイムゾーンやロケールなどシステムに関わる内容が含まれています。基本的にはLaravelの`config/app.php`の内容と同じはずですが念のため内容を確認される事をお勧めします。


# Laravelのリソースを使う

Laravelとの連携が完了した事で今後はLaravelのリソースが使えるようになります。
せっかくなのでログ出力をLaravelの`Logger`を使って出力してみます。
以降ではデモ版のWebsocketサーバーを使います。

デモ版の内容は以下の記事でご紹介しています。

https://qiita.com/glasses_seven/items/7a490dc8694348505a53/

まずは初期化クラス内で`use Illuminate\Support\Facades\Log;`を追加した上で`logWriter`ハンドラーを以下のように修正します。
以下のコメントで囲んでいる部分が元のソースです。

```php:app/InitClass/InitForWebsocket.php
public function getLogWriter()
{
    return function(string $p_level, array $p_param)
    {
        Log::channel('socket-manager-log')->{$p_level}('TEST', ['param' => $p_param]);

        /*
        $filename = date('Ymd');
        $now = date('Y-m-d H:i:s');
        $log = $now." {$p_level} ".print_r($p_param, true)."\n";
        error_log($log, 3, "./logs/socket-manager-log/{$filename}_W{$this->port}.log");
        */
    };
}
```

Laravelの`config/logging.php`内で以下の定義を追加してサーバー起動します。

```text:config/logging.php
'socket-manager-log' => [
    'driver' => 'daily',
    'path' => storage_path('logs/bat/socket-manager-log/laravel.log'),
    'level' => 'debug',
    'days' => 0,
],
```

```terminal:デモ版Websocketサーバー起動（10000ポートで起動する場合）
> php worker app:chat-server 10000
```

クライアントと接続すると以下の場所にログファイルが作成されます。

```text:ログファイルの出力先
/storage
    /logs
        /bat
            /socket-manager-log
                laravel-<日付フォーマット（"Y-m-d"）>.log
```


# おわりに

これでWebアプリとソケット通信サーバーのオールインワン開発環境ができました。
上記ではLaravelのログ出力を試しましたが、他にも色々使えるはずですので試してみてください。

生のPHP環境でデータベースを使う場合は標準のPDOライブラリ等を使う事が多いと思いますが、フレームワークの作業に慣れている方はEloquentやクエリビルダ等を使ってデータベースと連携した方が使いやすいでしょう。

ここで使用したLaravelは最近のバージョンですが、PHPのバージョンがv8.1.0以降で動作している環境であれば問題ないと思います。