Virionを使ってプラグインを開発してみる

※この記事ではVirionを使ったプラグイン開発について解説しています。
Virion自体を作成する方法については解説していません。
ある程度の英語力と基礎知識がある方は、こちらを見た方が早いかもしれません。
[参考リンク](#参考リンク)

# そもそもVirionとは？
主にPMMPプラグインで使用されるライブラリのフレームワークです。
PHPで使われるcomposerのPMMP版に近いです。
ライブラリをVirion(和訳するとウイルスのような意味)として考えます。
「プラグインにウイルス(ライブラリ)を注入する」といった感じです。

# Virionを使用するメリット
従来はプラグインでライブラリ(例えばFormAPI)などを使用したいときに、利用者側で別途ライブラリプラグインを導入する必要がありました。
Virionをプラグインのpharに注入することで、そのような事をする必要がなくなります。
もちろん外部ライブラリを使用したプラグインを作る知識は必要ですが、Virion自体は初心者でも思ったより簡単に使えます！

# 名前空間のシェーディング
Virionの仕組みは以下で説明する「名前空間のシェーディング」によって成り立っています。

ライブラリとして共通の`foo\bar\API`があるとします。
プラグインのメインクラスは`プラグイン名\Main`とします。
Virionのepitopeは`libs`とします。

プラグインAに`foo\bar\API`クラスがあって、それをプラグインBでも定義した場合同じ`foo\bar\API`となりクラスが重複してしまうことになります。

PHPはプラグインAとBどちらの`foo\bar\API`クラスを指すのか分からなくなってしまい、クラスの中身が違う場合(バージョンが違うVirionを使用した場合など)に問題が出てきます。

```:不適切な例
PluginA/
　├ src/
　│　├ foo/
　│　│ 　└ bar/
　│　│ 　　└ API.php # 重複する！
　│　└ PluginA/
　│　　　└ Main.php 
　└ plugin.yml

PluginB/
　├ src/
　│　├ foo/
　│　│ 　└ bar/
　│　│ 　　└ API.php # 重複する！
　│　└ PluginB/
　│　　　└ Main.php 
　└ plugin.yml
```

それを解決するために、Virionでは名前空間とファイル構成を変更します。

それにより、プラグインAでは`PluginA\libs\foo\bar\API`、プラグインBでは`PluginB\libs\foo\bar\API`として使えるようになって、クラスが重複しなくなります。

```:Virionを注入した場合の例
PluginA/
　├ src/
　│　└ PluginA/
　│　　　└ libs/
　│　　　　　└ foo/
　│　　　　　 　└ bar/
　│　　　　　  　　└ API.php
　│　　　└ Main.php 
　└ plugin.yml

PluginB/
　├ src/
　│　└ PluginB/
　│　　　└ libs/
　│　　　　　└ foo/
　│　　　　　 　└ bar/
　│　　　　　  　　└ API.php
　│　　　└ Main.php 
　└ plugin.yml
```

この処理はVirionを注入時に勝手に行ってくれるので、開発する上でこれを気にすることはありません。

例えば、`use foo\bar\API`は注入時に自動で`use PluginA\libs\foo\bar\API`(プラグインAの場合)に置換されます。

後述するDEVirionを使用する場合にはこのような名前空間のシェーディングが行われず単に読み込むだけなので、ディレクトリ構成や名前空間は変更されません(前述の不適切な例と同じ事を起こします)。
そのため前述したクラスの重複などの問題を引き起こす可能性があります。それがDEVirionを開発環境以外で使用してはいけない理由です。

# プラグインにVirionを注入する方法

## Poggit CIを使用する

この方法が使用できるのは、対象VirionがPoggit CIでビルドされている場合に限ります。

```yaml:.poggit.yml
projects:
  TargetPlugin:
    libs:
      - src: jojoe77777/FormAPI/libFormAPI # ユーザー名/リポジトリ名/Virion名
        version: 1.2.0 # バージョン
```

※Virion名は`virion.yml`に記載の`name`、バージョンは`virion.yml`に記載の`version`

## [JackMD](https://poggit.pmmp.io/plugins/by/JackMD)氏の[VirionTools](https://poggit.pmmp.io/p/VirionTools/)プラグインを使用する

ここでは、[VirionTools](https://poggit.pmmp.io/p/VirionTools/)プラグインを使用して、`TargetPlugin`プラグインに[jojoe77777](https://github.com/jojoe77777)氏の[libFormAPI](https://poggit.pmmp.io/ci/jojoe77777/FormAPI/libFormAPI)を注入します。

### 準備
1. [VirionTools](https://poggit.pmmp.io/p/VirionTools/)をサーバーに導入します。
2. `plugin_data\VirionTools\builds`にプラグインに注入したいVirionのpharファイル(`libFormAPI.phar`など)を入れます。
3. `plugin_data\VirionTools\plugins`にVirionを注入したいプラグインのpharファイル(`TargetPlugin.phar`など)を入れます。

### Virionを注入する
1. `/iv <Virionのファイル名(拡張子は省略可能)> <プラグインのファイル名(拡張子は省略可能)>`コマンドをサーバーで実行します。(例: `/iv libFormAPI.phar TargetPlugin.phar`)
2. 指定したプラグインのpharファイルにVirionが注入されます。

この手順を繰り返すことで、複数のVirionを注入することもできます。

### 必要な全てのVirionを注入する
1. Virionを注入したいプラグインのplugin.ymlに、使用したいVirionの名前を`virions`をキーにして配列で入れます。
2. `/ia <プラグインのファイル名(拡張子は省略可能)>`コマンドをサーバーで実行します。(例: `/ia TargetPlugin.phar`)
3. 指定したプラグインのpharファイルに全てのVirionが注入されます。

例

```yaml:plugin.yml
name: TargetPlugin
version: 1.0.0
main: PJZ9n\TargetPlugin\Main
api: 3.0.0
virions:
  - libFormAPI
  - HogeAPI
```

# DEVirionを使用して開発の効率を上げる
これまでの方法だけだと、プラグインの動作確認をするだけでも毎回Virionを注入する必要があり、とても不便です。
[poggit](https://poggit.pmmp.io/plugins/by/poggit)の[DEVirion](https://poggit.pmmp.io/p/DEVirion/)を使用することによって、その環境ではVirionを注入しなくてもVirionが使用できるようになります。

※この方法は絶対に開発用途以外で使用しないでください。Virionの仕組み自体が無意味になってしまいます。
(具体的な問題としては、異なるバージョンの同じVirionを使用したプラグインがあった時などに互換性を維持出来なくなります。[#名前空間のシェーディング](#名前空間のシェーディング))

## 使用方法
1. [DEVirion](https://poggit.pmmp.io/p/DEVirion/)をサーバーに導入します
2. サーバーを起動して停止します。
3. 生成された`virions`ディレクトリ(`plugins`と同じ場所)の中に、Virionのpharファイル(`libFormAPI.phar`など)を入れます。

※DevToolsのようにフォルダ形式のvirionライブラリも入れることもできます。

このようにすることで、毎回プラグインにVirionを注入しなくてもライブラリが使用できるようになります。

# 参考リンク
- [Virion 公式ドキュメント](https://poggit.github.io/support/virion.html)
- [VirionTools README](https://github.com/JackMD/VirionTools/blob/master/README.md)
- [DEVirion README](https://github.com/poggit/devirion/blob/master/README.md)

# あとがき
記事中に間違っている部分や不適切な部分があれば教えてください。お役に立てたらLGTMして頂けるとありがたいですm(__)m