CakePHPのプラグインをOSSにする時に考えたいこと 〜muffin/skeletonを題材に〜

[CakePHP Advent Calendar 2019 \- Qiita](https://qiita.com/advent-calendar/2019/cakephp)の8日目です！！！
(めちゃくちゃ遅刻しておりますが・・


私は過去にCakePHP関連のOSSをいくつか作成したことがありますが、「本質的な機能・設計」と同時に「公開するに当たってのお作法」というのも気になるものです。

そういえば、 先日のCakeFest にて @itoshoさんの「Let's start your first OSS with CakePHP」という題での発表もありました。
当記事のタイトルにご興味を持った方でしたら、こちらも是非1度御覧ください！
[Let's start your first OSS with CakePHP \- Speaker Deck](https://speakerdeck.com/itosho525/lets-start-your-first-oss-with-cakephp)

さて、この「お作法」には、例えばCIのセットアップや、testにlinter、そしてPHPパッケージの公開についてはcomposer.jsonの作成もいるでしょう。
ボイラーテンプレートを一旦作ってしまえば楽かもしれませんね。その1歩目をどう踏み出すのか・・・

ということで、今回紹介するのは [muffin/skeleton](https://packagist.org/packages/muffin/skeleton)です！

## muffin/skeletonってなに？
_A plugin skeleton builder for CakePHP 3 using Composer._ と書いてあります。
「対話的に質問に答えていくと、CakePHPプラグイン用の空プロジェクトが出来上がっている！」とうい代物です。

GitHubレポジトリはこちら。
https://github.com/UseMuffin/Skeleton

実際に実行している様子がgifアニメでREADMEに載っているので覗いてみてください。

## どう使うの？
`composer create-project` コマンドにて実行します。
例えば `oasobi-plugin` というプラグインを作成するのであれば、以下のようになります

```php
composer create-project muffin/skeleton oasobi-plugin --ignore-platform-reqs
```
※PJ作成時には、実際に開発したり実行をする環境が整っていないことも多いかと思うので `--ignore-platform-reqs` を付けています。この辺りは個人的な好みです。

すると、いくつかの質問をされて、答えていくと良い感じなcomposer.jsonが出来上がります！ :tada: [^unfixed]
それに加えて、(対話時の入力内容によって)「config」「migration」「routing」辺りもこなしてくれて、素敵 :smile: という訳です。

[^unfixed]: 本当はREADMEが作成されたり、また不要なファイルの削除処理も走るのかな？と思いますが、今は動いていないかもです・・・READMEについてはIssueも立っています。 https://github.com/UseMuffin/Skeleton/issues/7

### 便利なの？
「初手としては有効」と思います。これで作ったものを、カスタマイズしていくと少し楽でしょう。
「composer.jsonを作る」のは`composer init`を実行することで担えますが、これが「CakePHPを想定してカスタマイズされている」と思ってください。
例えば `type: cakephp-plugin`の(自動)を追記だったり `"keywords": ["cakephp", "plugin", "skeleton"]`が入っていたりするのは「らしい」と言えるのではないでしょうか[^skelton]。また、`support`セクション等の自動追記も良い感じです。

その他、`phpcs.xml` `vendor`(厳密にはrequire-dev)はそのまま流用をしてしまって良いと思います。[^phpcs]

[^skelton]: あれ、skelton・・？なんでだ？
[^phpcs]: `phpcs.xml` に _Include the whole PSR-2 standard_というコメントが入っていますが、今のmasterではPSR-12対応になりますので注意してください。see: https://github.com/cakephp/cakephp-codesniffer/pull/257

## 物足りないところはある？

### README
やはり、READMEが動かないのは勿体ないなぁという気はします(こういうのは、つべこべ言わずに動作を修正するPRを出せば良いですね！！)

本来であれば、以下のテンプレート通りにREADMEが作成されるものと思います
※Qiitaの記法の都合上、一部改変しています

```markdown
# __NAME__

[![Build Status](https://img.shields.io/travis/__GITHUB__/master.svg?style=flat-square)](https://travis-ci.org/__GITHUB__)
[![Coverage](https://img.shields.io/codecov/c/github/__GITHUB__.svg?style=flat-square)](https://codecov.io/github/__GITHUB__)
[![Total Downloads](https://img.shields.io/packagist/dt/__PACKAGE__.svg?style=flat-square)](https://packagist.org/packages/__PACKAGE__)
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE)

__DESCRIPTION__

## Install

Using [Composer][composer]:

composer require __PACKAGE__:1.0.x-dev

You then need to load the plugin. You can use the shell command:

bin/cake plugin load __PLUGIN__

or by manually adding statement shown below to your app's `config/bootstrap.php`:

Plugin::load('__PLUGIN__');

## Usage

{{@TODO documentation}}

## Patches & Features

* Fork
* Mod, fix
* Test - this is important, so it's not unintentionally broken
* Commit - do not mess with license, todo, version, etc. (if you do change any, bump them into commits of
their own that I can ignore when I pull)
* Pull request - bonus point for topic branches

To ensure your PRs are considered for upstream, you MUST follow the [CakePHP coding standards][standards].

## Bugs & Feedback

http://github.com/__GITHUB__/issues

## License

Copyright (c) __YEAR__, __AUTHOR__ and licensed under [The MIT License][mit].

[cakephp] :http://cakephp.org
[composer] :http://getcomposer.org
[mit] :http://www.opensource.org/licenses/mit-license.php
[standards] :http://book.cakephp.org/3.0/en/contributing/cakephp-coding-conventions.html
```

当然ながら、このREADMEをそのままコミットして出来上がり！という訳には行きませんが、「どんなPlugin/Packageでも利用しそうな内容」については抑えているなぁと感じます。badgeを良い感じにつけてくれるのも気が利いていますよね。

### インストール後の不要ファイルについて
現状だと `Muffin\Skeleton\Console\Installer` がPJに居座りますが、このファイル自体とcomposer.jsonのscriptsは不要なはずです。
[post-install-cmd](https://github.com/UseMuffin/Skeleton/blob/v1.0.1/src/Console/Installer.php#L114-L132)と[composer.jsonファイルの修正](https://github.com/UseMuffin/Skeleton/blob/v1.0.1/src/Console/Installer.php#L326-L327)が効いていない・・？

この辺りは、手動で修正しちゃうのが良さそうです。(これもまた、PRを投げたいところですね！)

### testについて
phpunit/phpunitがrequire-devで入るので、`tests`ディレクトリとphpunit.xmlの作成まで行なってしまって良いのかな？という気が、個人的にはしています。
モノによる・・というのは承知しておりますが、多くの場合は、CakePHPプラグインの作成にあたっては「CakePHPそのものを起動できるようにbootstarapファイルを用意する」ことになると思います。
例えば、Bakeプラグインの場合には以下のように設定がされています。

https://github.com/cakephp/bake/blob/2.0.0/tests/bootstrap.php
やっている内容としては「CakePHPにまつわる初設定を行い、\Bake\Pluginを登録する」というものです。ここまでは、大体のプラグインで使うのかなーと思います。ということで、skeltonとして設置してくれるといいな！と思うのでした。

## その他に「CakePHPプラグインを公開するに当たって、個人的に思うこと」
以下は、「Muffinのプラグインの範疇ではないかもな！」と感じつつ、「自分で公開する際にはここもポイントとして抑えるかな！」というところです。

### 静的解析について
CakePHP本体が静的解析をより活用していく方向性に有るので、プラグイン側でもまた静的解析を活用していくのがベターかな、と個人的には考えています。
CakePHP4を見越すのであれば、PHPStanのLv.5 + Psalmへの対応でしょうか。
設定内容についてはCakePHP本体のやり方を参考にしつつ、「同じ水準で」クリアしていけると、開発者自身も利用者にとっても少し安心感が出るのではないかと思います。

### CI実行時のprefer-lowest
composer updateのオプションに `prefer-lowest` があります。
これは、「composer.jsonで指定されている、最も低いバージョンのバッケージを取得する」という指定です。

実際にCakePHPでも利用しているCI戦術で、詳細なイメージについてはこちらのPRをご参照ください。
[Include prefer lowest and simplify checks by dereuromark · Pull Request \#12746 · cakephp/cakephp](https://github.com/cakephp/cakephp/pull/12746)

例えば `cakephp/cakephp:^3.6.0`というパッケージを指定した場合、そのソフトウェアは「3.6.0でも、3.8.7でも動く必要がある」という事になります。しかし、単純に`composer install(ないしupdate)`をした場合には、3.x系の最新バージョンで動作をさせることになります。それだけでは「`^3.6.0`という依存条件を満たしているとは言えない」という訳です。

そこで、「最新バージョンでも最小バージョンでも動くこと」を実際に検査する必要が出てきます。それが `--prefer-lowest`によって実現できることです。具体的には、CI上で「オプションあり・なし」の双方でのビルドを実行させてください。(min/maxの両方さえ満たしていれば、中間バージョンは省略して問題ないと考えます)

例えば、TravisCIの場合は以下のような記述をイメージしてみてください。

```yaml
php:
  - 7.0
  - 7.1
  - 7.2
  - 7.3
  - 7.4

env:
  global:
    - DEFAULT=1 PREFER_LOWEST="--prefer-lowest"

matrix:
  fast_finish: true

  include:
    - php: 7.4
      env: PREFER_LOWEST=""

before_script:
  - composer update --prefer-stable --prefer-dist --no-interaction $PREFER_LOWEST
```

そうすると、「7．0-7．4のPHP各バージョンにおいて、最小バージョンのパッケージ構成で」&「PHP7．4において、最新バージョンのパッケージ構成で」のずべ手を満たす形でCIを回すことが出来ます。

### 実行・開発環境について
こちらは完全に趣味と呼ぶべき範疇かもしれませんが・・・

スタンドアロンなフレームワークやプラグインを開発していくに当たって、いつも「どうやって動かせばいいのかな」という所で手が止まってしまいがちです。とくに、久しぶりにいじるか！！となった時に、うまく動かない〜〜というのはちょっと心が折れるものです。

そこで、最近はdoker-compose.ymlを同梱して、「開発環境も一緒に配れるようにする」という手段をとっています。

例えば、このような感じです。
https://github.com/Connehito/cakephp-master-replica/blob/1.0.0/tests/test_app/docker-compose.yml

これによって、「cloneしてすぐにテストを叩いてみる！」という体験を実現できるわけです。
.gitattributesの設定を適切に行うことでcomopser install/require時に「余計なファイルが入る」問題も防げますので、こうした「無くても良いけど有ると良いかもね」というモノも気兼ねなく扱えるはずです。


## まとめ
「いつも似たようなことをする」のであれば、やはりエンジニアとしては自動化ですね :muscle: 
とはいえ「実行回数はそんなに多くないのに、その作業を自動化するツールを作る」のでは本末転倒です。
そんな時に便利そうなものがあるよ、ということでmuffin/skeletonの紹介でした！ :tada: