Composer によるパッケージのアップデート入門

## はじめに

「パッケージをアップデートしたらなんかエラー出た」

なんてことにならないために、雰囲気でやっている Composer によるパッケージのアップデートをちゃんと理解しながら行いたい。そんな人向けの記事になります。

## 環境

- Composer 1.10.x (Installed with Homebrew)

## 事前知識

- PHP と Framework のバージョン
- セマンティック・バージョニング
- Composer によるバージョン指定方法いろいろ
- composer.lock ファイル

入門なんで細かなことは抜きにしたいですが、最低でも上記の 4 つはおさえておきましょう。

### PHP と Framework のバージョン

「フレームワークをメジャーアップデートしたらなんかエラー出た」。雰囲気でやっていると起こりうるものですが、特にメジャーアップデートは互換性のない変更が含まれている可能性が高いので、アップデートを行う前に、どのような変更がなされているのか、どういう風に修正していけばいいのか、事前に公式ドキュメントをしっかり確認しておくこと。

また、フレームワークのメジャーアップデートは PHP の最低バージョンの要求も変わってくる可能性があるので、そこもしっかり確認しておくこと (拡張モジュールやミドルウェアも含む) 。

それらをクリアした上でアップデートを行い、プログラムの修正をしていきます。これで「なんかエラー出た」ということは少なくなりますし、ユニットテストなどを書いていれば尚良し、よりスムーズにメジャーアップデート作業はできるかと思います。

あと欲を言えば、開発/ステージング/本番環境で使用する言語やミドルウェアのバージョンを合わせておくのが理想ですが、それはまた別のところで。

備考: フレームワークによっては LTS バージョン (長期サポート版) が提供されているものもあるので、もしあればそれを選ぶことをおすすめします (メジャーアップデートによる大幅な修正/変更回数を減らせるため) 。またメジャーアップデート版がリリースされたからといってすぐに飛びつくのではなく、ある程度期間をあけてパッチバージョンがいくつか出てから手を出したほうがよりバグを踏むことが少なくなるのでおすすめです。

### セマンティック・バージョニング

(セマンティック・バージョニングがわからない人はまずは[これ](https://semver.org/lang/ja/)を読んでください)

依存しているパッケージがセマンティック・バージョニングの取り決めに従っているかは実際のところパッと見ではわかりません。ということは、とあるパッケージのパッチバージョンをアップデートしただけで互換性のない変更が行われ「エラーが出てしまった」ということもあり得るわけです。

じゃあどのように対策すればいいかというわけですが、基本的には使用しているフレームワークのバージョン指定方法を変えないこと。例えば Laravel の 5.5 系だと composer.json には `"laravel/framework": "5.5.*"` と書かれており、7.x 系だと `"laravel/framework": "^7.0"` と書かれています。これにはちゃんと意味があり、互換性を保つための指定方法で書かれています。なのでこの場合は雰囲気でパッチバージョンのアップデートをしてもそうそう壊れることはありません (フレームワーク側のバグで壊れることもありますが...) 。

またフレームワークに関連するパッケージも初期の指定方法から変えないことで安定したアップデートができるかと思います。

あとは composer.json に書かれているパッケージすべてにおいてセマンティック・バージョニングの取り決めに従っているかを GitHub なり公式ドキュメントで確認する他ありません。しっかりやっておきましょう。

特に注意すべき点は、セマンティック・バージョニングの取り決めに従っていたとしても、バージョンが 0 系ならマイナー/パッチバージョンのアップデートでも互換性のない変更が行われる可能性があるということです。
しっかり把握しておきましょう。

### Composer によるバージョン指定方法いろいろ

Composer のパッケージのバージョン指定方法にはいくつかのやり方があり、正直とてもわかりにくいです。ただここがわからないと雰囲気でアップデートするはめになるので、しっかり覚えておきましょう。具体的には以下:

```
{
  "require": {
    "vendor/package": "1.3.2", // 1.3.2
    "vendor/package": ">=1.3.2", // 1.3.2 以上
    "vendor/package": "<1.3.2", // 1.3.2 未満
    "vendor/package": "1.3.*", // 1.3.0 から 1.4.0 未満
    "vendor/package": "~1.3.2", // 1.3.2 から 1.4.0 未満
    "vendor/package": "~1.3", // 1.3.0 から 2.0.0 未満
    "vendor/package": "^1.3.2", // 1.3.2 から 2.0.0 未満
    "vendor/package": "^1.3", // 1.3.0 から 2.0.0 未満
  }
}
```

キャレットでの指定方法はセマンティック・バージョニングの取り決めに従っているパッケージかな？と読み取ることもできますね (従っているかはわからない、あくまで読み取れるだけ) 。

指定方法はまだまだありますが、アップデートで指定するものは上記らへんかなと。その他は [Versions and constraints - Composer](https://getcomposer.org/doc/articles/versions.md) で確認できます。

### composer.lock ファイル

Composer による依存パッケージのインストール、およびアップデート/削除などを行うと composer.lock ファイルが自動で生成/更新されます。以降はパッケージの更新がない限り composer.lock ファイルに書かれたバージョンがインストールされます。

要は別の誰かが composer install しても composer.lock ファイルがあるかぎり同じバージョンのパッケージがインストールされるわけなので、パッケージの更新などをした場合でバージョン管理システムを使っているなら、composer.lock ファイルもガンガン add して commit して push しておきましょう。僕と誰かしらを繋ぐとても重要なファイルなのです。

## パッケージのアップデートをしてみる

事前知識が長くなってしまいましたが、では実際にパッケージのアップデートをやっていきましょう。

以下のようにします...か？:

```
composer update
```

うーん...ちょっと待った。違いますよ。上記のような大雑把なアップデートはなるべく避けましょう。

まずはどのパッケージがアップデート可能な状態であるか以下のコマンドで確認します:

```
composer outdated -D
```

で、個別にパッケージのアップデートをやっていきます:

```
composer require "vendor/package:^1.3" --update-with-dependencies
```

開発環境でのみ使用するパッケージには `--dev` を付けます:

```
composer require --dev "phpunit/phpunit:^8.0" --update-with-dependencies
```
で、ユニットテストなどを走らせてバグがないか確認していきます。で、また次のパッケージのアップデートをやっていきます。以下のようなことを繰り返していく感じです:

```
composer outdated -D
composer require "vendor/package:^2.1" --update-with-dependencies
vendor/bin/phpunit
```

いきなり `composer update` をやってしまうと、もしバグなどが発生した場合にどのパッケージによるバグなのか把握するのに時間がかかるので、そこは踏みとどまって、しっかり個別にアップデートを行い、正常に動作しているか確認した後に、また他のパッケージのアップデートをやる、という流れでやっていきましょう。

また、互換性のない変更があった場合に、パッケージによっては標準出力にて Changelog などを流してくれるものもあるので、これもしっかり確認しておきましょう。

備考: 個人的に一番確実なのは、パッケージを個別にアップデートするのと、GitHub のコミットや Changelog などで変更を確認しつつ、パッチバージョンであってもひとつひとつバージョンを決め打ちして上げていくことです:

```
composer outdated -D
composer require "vendor/package:1.3.2" --update-with-dependencies
vendor/bin/phpunit
```
こうすることによって、どのバージョンでバグが発生したかわかりますし、どのパッケージがセマンティック・バージョニングの取り決めに従っているかどうかも把握しなくていいし結果的には楽です。ただ依存するパッケージの数が多いとちょっとめんどくさいのと、バージョンの指定が手入力になるので、最悪の場合、脆弱性の含んだバージョンを誤って指定してしまうこともなくはないので (自分は `composer outdated -D` で出力されたものをコピペしてるので安心...でもないが)、プロジェクトによってはおすすめできない。あとはアップデートをする前に GitHub の issue を確認しておくこと。意外とパッチバージョンのアップデートでもバグは発生します...。

### まとめ

一つのアプリケーションは非常に多くのパッケージに依存しているし、機能が多ければ多いほどプログラムもより複雑になり、そんな中でバグを 0 % にするのは非常に難しいとは思いますが、雰囲気でやっているあれやこれやを少しでも減らすことによって、どのパッケージのバグであるかなどの把握に繋がることもあります。そういう意味で、あらためて、Composer によるパッケージのアップデート入門という記事を書いてみました。

でもまだまだ雰囲気ですよ。完全に理解なんかできない。

### 関連リンク
[セマンティック バージョニング](https://semver.org/lang/ja/)
[Basic usage - Composer](https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies)
[Versions and constraints - Composer](https://getcomposer.org/doc/articles/versions.md)