VSCode RemoteContainersでmoodleプラグイン開発環境を作った

# 概要

お仕事でmoodleプラグインを開発するにあたり、ローカルにApacheやらPHPやら色々入れるのが面倒だった[^1]ので[VSCode RemoteContainers](https://code.visualstudio.com/docs/remote/remote-overview)を使ってサクッと作って壊せる開発環境を作った。

プラグインの開発環境としてだけではなく、ローカルでmoodleを動かす用途にも使えます。

# 作成したもの

[aoisupersix/moodle-plugin-devcontainer](https://github.com/aoisupersix/moodle-plugin-devcontainer)

プラグイン自体は[moodle公式のチュートリアル](https://docs.moodle.org/dev/Blocks)にあった固定のHTMLを表示するブロックプラグインです。

# 動かし方

1. [Docker](https://docs.docker.com/get-docker/),[VSCode](https://code.visualstudio.com/download),[RemoteContainers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)を事前にインストール
2. リポジトリをローカルに持ってきてVSCodeで開く

    ```
    git clone https://github.com/aoisupersix/moodle-plugin-devcontainer
    code moodle-plugin-devcontainer
    ```

3. VSCode上で`Remote-Containers: Open Workspace in Container...`コマンドを実行しコンテナのセットアップを行う

4. `http://localhost:8080`を開いてmoodleのインストールを行う

以上。楽ちん。

# コンテナの構成

コンテナは

- Apacheとmoodleを動かすメインのコンテナ（VSCode上で操作するコンテナ）
- MariaDB
- phpMyAdmin

の3つに分かれており、それらの定義は `.devcontainer/docker-compose.yml`にあります。

```yaml
version: "3"
services:
  mariadb:
    image: "docker.io/bitnami/mariadb:10.5-debian-10"
    user: root
    environment:
      - ALLOW_EMPTY_PASSWORD=yes
      - MARIADB_USER=moodle
      - MARIADB_DATABASE=moodle_db
    volumes:
      - "./container-volumes/mariadb_data:/bitnami/mariadb"
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=mariadb
      - PMA_USER=moodle
      - PMA_PASSWORD=
    ports:
      - 8081:80
    volumes:
      - "./container-volumes/phpmyadmin_sessions:/sessions"
    depends_on:
      - mariadb
  moodle:
    build:
      context: ./moodle
      args:
        - MOODLE_VERSION=v3.10.0
    environment:
      - MOODLE_DBTYPE=mariadb
      - MOODLE_DBHOST=mariadb
      - MOODLE_DBNAME=moodle_db
      - MOODLE_DBPORT=3306
      - MOODLE_DBUSER=moodle
      - MOODLE_DBPASSWORD=
      - MOODLE_WWWROOT_URL=http://localhost:8080
    ports:
      - "8080:80"
    volumes:
      - "./moodle/php.ini:/usr/local/etc/php/php.ini"
      - "./moodle/config.php:/var/www/html/config.php"
      - "./container-volumes/moodledata_data:/var/www/moodledata"
      - "./container-volumes/phpunitdata_data:/var/www/phpunitdata"
      - "..:/var/www/html/blocks/simplestatichtml:cached" # Workspace directory in the container. /var/www/html/${plugin_type}/${plugin_name}
    depends_on:
      - mariadb

```

プラグインのコードが含まれるボリュームは特にDocker for Macで動作が遅くなるのを低減するために`:cached`でマウントしてます。

```
      - "..:/var/www/html/blocks/simplestatichtml:cached"
```

MariaDBとphpMyAdminは公式で用意されているイメージをそのまま使っているだけですが、moodleについては開発用に適しているイメージが見つからなかったので自前で定義しました。

## moodleコンテナ

`.devcontainer/moodle/Dockerfile`

```docker
FROM moodlehq/moodle-php-apache:7.4

ARG MOODLE_VERSION="v3.10.0"

ENV MOODLE_DBTYPE mariadb
ENV MOODLE_DBHOST localhost
ENV MOODLE_DBNAME moodle
ENV MOODLE_DBUSER moodle
ENV MOODLE_DBPASSWORD password
ENV MOODLE_PREFIX mdl_
ENV MOODLE_DBPORT 3306
ENV MOODLE_WWWROOT_URL http://localhost

# Set timezone
ENV TZ Asia/Tokyo
RUN echo "${TZ}" > /etc/timezone \
    && dpkg-reconfigure -f noninteractive tzdata

# Set locale
RUN echo 'ja_JP.UTF-8 UTF-8' >> /etc/locale.gen \
    && locale-gen \
    && update-locale LANG=ja_JP.UTF-8

# Install composer
COPY --from=composer /usr/bin/composer /usr/bin/composer

# Install xdebug
RUN pecl install xdebug-3.0.4 \
    && docker-php-ext-enable xdebug

USER www-data

# Clone moodle
RUN git clone https://github.com/moodle/moodle -b $MOODLE_VERSION --single-branch --depth 1 .

USER root
```

ApacheとPHPが入っており、moodleの各種ディレクトリがセットアップ済みの[moodlehq/moodle-php-apache](https://github.com/moodlehq/moodle-php-apache)をベースにしています。

やっていることは

- タイムゾーンとロケールの設定
- composerのインストール（PHP_CodeSniffer等のインストールに必要なため）
- xdebugのインストール
- moodle本体のclone

です。

### 環境変数について

moodleの設定で今後変更される可能性がありそうな項目については環境変数として定義して`docker-compose.yml`側で変更できるようにしています。

- `.devcontainer/moodle/Dockerfile`

```
ENV MOODLE_DBTYPE mariadb
```

- `.devcontainer/docker-compose.yml`

```
environment:
      - MOODLE_DBTYPE=mariadb
```

moodleの設定は`.devcontainer/moodle/config.php`で、`getenv()`を使用して定義した環境変数を設定するようにします。

```
$CFG->dbtype = getenv('MOODLE_DBTYPE');
```

### moodleインストール前にユーザを変更している理由

`.devcontainer/moodle/Dockerfile`では、以下のようにユーザを`www-data`に変更してからmoodleをcloneしてますが、その理由について。

```
USER www-data

# Clone moodle
RUN git clone https://github.com/moodle/moodle -b $MOODLE_VERSION --single-branch --depth 1 .

USER root
```

moodleコンテナではApacheの実行ユーザが`www-data`となっており、デフォルトのユーザである`root`でcloneしてしまうとmoodleディレクトリの書き込み権限がない状態になってしまうためです。

moodleをcloneした後に`chown -R www-data:www-data /var/www/html`を実行する、という方法でも対応可能ですが`chown`にめちゃくちゃ時間がかかるため上記のユーザを切り替える方法に落ち着きました。

## phpMyAdminについて

MariaDBの操作をGUIで行える便利なツールで、開発用コンテナと一緒に動かしています。

`http://localhost:8081`からアクセスできます。

# PHPのコードチェッカーとIntelliSenseのサポート

- コードチェッカーは[PHP Sniffer](https://marketplace.visualstudio.com/items?itemName=wongjn.php-sniffer)
- IntelliSenseは[PHP Intelephense](https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client)

を使ってます。

これらの拡張機能は`devcontainer.json`に記載してコンテナ初期化時に自動でインストールするようにしてます。

`.devcontainer/devcontainer.json`

```
    "extensions": [
        "wongjn.php-sniffer",
        "bmewburn.vscode-intelephense-client",
```

また、`settings.json`でこれらの拡張機能の設定を記載してます。

`.vscode/settings.json`

```
{
    "intelephense.environment.includePaths": [
        "/var/www/html"
    ],
    "phpSniffer.autoDetect": true,
    "phpSniffer.run": "onType",
    "[php]": {
        "editor.defaultFormatter": "wongjn.php-sniffer"
    }
}
```

`intelephense.environment.includePaths`にmoodleのルートディレクトリを指定することで、プラグインのコードに限らずmoodleのコード全体のコード補完が効くようにしてます。

コードチェッカーについては、moodleのコーディング規約が独特（ex. メソッドの命名規則がスネークケース等）なため、moodleコードチェッカープラグインのルールセット：[ruleset.xml](https://github.com/moodlehq/moodle-local_codechecker/blob/master/moodle/ruleset.xml)を使用しています

# xdebugでのデバッグ対応

[xdebug](https://xdebug.org/)はPHPのデバッグ等を可能にするツールです。
これを利用することでブレークポイントを貼って変数の中身を確認したり、ということが可能です。

xdebugのバージョンは現時点で最新の3.0.4を使用しています。
2.xと3.xで設定項目が全く異なるので注意が必要。

1. moodleコンテナにxdebugをインストール

    `.devcontainer/moodle/Dockerfile`

    ```
    RUN pecl install xdebug-3.0.4 \
        && docker-php-ext-enable xdebug
    ```

2. php.iniでxdebugのポートや自動起動の設定を行う

    `.devcontainer/moodle/php.ini`

    ```
    [xdebug]
    xdebug.mode=debug
    xdebug.start_with_request=yes
    xdebug.client_port=9000
    xdebug.log=/tmp/xdebug.log
    ```

    ポートは9000を使用しています。
    また、`xdebug.start_with_request=yes`を追加してデバッガを常に動かすように設定してます。

3. VSCodeにxdebugの拡張機能をインストール

    [PHP Debug](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug)の拡張機能をVSCodeにインストールします。
    今回の環境では`devcontainer.json`にPHP Debugの拡張機能を記載してコンテナ初期化時に自動でインストールされるようにしてます。

    `.devcontainer/devcontainer.json`

    ```
    "extensions": [
        "felixfbecker.php-debug",
    ```

4. VSCodeからxdebugのポートに接続

    `launch.json`にxdebugのポートに接続する設定を記載します。

    `.vscode/launch.json`

    ```
    "configurations": [
        {
            "type": "php",
            "request": "launch",
            "name": "XDebug on DevContainer",
            "port": 9000,
            "hostname": "localhost",
            "pathMappings": {
                "/var/www/html/blocks/simplestatichtml": "${workspaceFolder}" // Workspace directory in the container. /var/www/html/${plugin_type}/${plugin_name}
            }
        },
    ```

    後はVSCodeで「Xdebug on DevContainer」を実行するとデバッグを有効にすることが可能です。

    ![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197871/b9a31693-53b2-f409-6c94-3c099460aff6.png)

デバッグはプラグインのコードのみではなくmoodleの全コードに対して有効です。
例えば、ログイン時の動作を確認したい場合は`/var/www/html/login/index.php`を開いてブレークポイントを貼れば処理の動きが確認できます。

# moodleの設定

前述の通り`.devcontainer/moodle/config.php`に定義しており、コンテナの`/var/www/html/config.php`にマウントしています。

### デバッグメッセージの表示

以下を追加しておくことで通常は表示されないデバッグメッセージや警告が表示されるようになります。
開発時に便利なので有効にしています。

```
// Debug display settings
// NOT FOR PRODUCTION SERVERS!
@error_reporting(E_ALL | E_STRICT);
@ini_set('display_errors', '1');
$CFG->debug = (E_ALL | E_STRICT);
$CFG->debugdisplay = 1;
```

### 管理者設定

環境作成の度にmoodleのUI上で設定が必要になり面倒な設定項目については、`config.php`上で定義しています。
例えば、サイトのデフォルト言語や管理者ユーザ名など。

これら以外でも管理者設定項目であれば何でも設定可能（なハズ）です。

```
// Admin settings
$CFG->lang = 'ja';
$CFG->admin = 'admin';
$CFG->noreplyaddress = 'noreply@example.com';
```

なお、管理者ユーザのメールアドレスやパスワードも`config.php`で定義できると便利なんですが、これは不可能だったので毎回手動で設定しています。

# メール送信の対応

[mailhogコンテナ](https://hub.docker.com/r/mailhog/mailhog/)を一緒に動かしてメールサーバの設定を行えばmoodleからメール送信のテストが可能になります。

- `.devcontainer/docker-compose.yml`

```
services:
  mailhog:
    image: 'mailhog/mailhog'
    ports:
      - '8025:8025'
```

- `.devcontainer/moodle/config.php`

```
$CFG->smtphosts = 'mailhog:1025';
```

# プラグイン種別やプラグイン名の変更

moodleのプラグインは、[プラグイン種別](https://docs.moodle.org/dev/Plugin_types)ごとにmoodleの指定位置にプラグインを配置することで有効になります。
そのため、プラグイン種別やプラグイン名を変更する場合はそれに伴ってコンテナ等の設定変更が必要になります。

具体的には、以下3箇所の書き換えが必要です。

- `.vscode/launch.json`

```
            "pathMappings": {
                "/var/www/html/blocks/simplestatichtml": "${workspaceFolder}" // Workspace directory in the container. /var/www/html/${plugin_type}/${plugin_name}
            }
```

- `.devcontainer/devcontainer.json`

```
    "workspaceFolder": "/var/www/html/blocks/simplestatichtml", // Workspace directory in the container. /var/www/html/${plugin_type}/${plugin_name}
```

- `.devcontainer/docker-compose.yml`

```
    volumes:
      - "..:/var/www/html/blocks/simplestatichtml:cached" # Workspace directory in the container. /var/www/html/${plugin_type}/${plugin_name}
```

# 開発環境の初期化

色々試しているうちに環境がグチャグチャになってしまった、等で環境を初期化したい場合について。
コンテナのボリュームが`.devcontainer/container-volumes`にあるので、それを削除すればOKです。

```
rm -rf .devcontainer/container-volumes
```

[^1]: 開発メンバーによってOSがWindowsだったりmacだったりとバラバラでそれぞれの構築手順を作成するのが面倒だったというのもある