Composerがパッケージのstabilityを解決するしくみ
PHPとComposerで先日composer/composer
のdev-master
に依存したプログラムを作っていたら、composer install
のときに以下のようなエラーがでてインストールできない問題にぶちあたりました。
Your requirements could not be resolved to an installable set of packages. Problem 1 - kohkimakimoto/altax v3.0.6 requires composer/composer dev-master -> no matching package found. ...
解決方法はcomposer.json
に"minimum-stability": "dev"
と"prefer-stable": true
を指定するか、対象のパッケージに"composer/composer": "@dev"
のようなstabilityフラグをつければOKでした。
さて、この件に関連する日本語情報があまりなかったのですが、 根本の仕組み(Composerがパッケージのstabilityを判断してダウンロードする仕組み)を丁寧に書いたブログをみつけたので、以下和訳してみました。
Composerのスタビリティフラグ
原題:Composer Stability Flags
https://igor.io/2013/02/07/composer-stability-flags.html
今のところcomposerのサポートにやってくる最もよくある問い合わせは、スタビリティ(パッケージの安定性、stability)がどう解決されるのかよくわからないというものだ。 よくこのケースは以下のような問い合わせになる。
パッケージB:dev-masterに依存するパッケージA:dev-masterを要求(require)したら、composerがパッケージBが見つからないというんだ。
ルートパッケージ
ルートパッケージはメインのcomposer.json
ファイルのことだ。これはcomposer install
を実行するときにいるディレクトリと同じディレクトリ内にある。
多くのcomposer.json
のフィールドはルートオンリーで、これはルートパッケージ内で指定されたときだけ影響をもつということだ。
ルートパッケージはコンテキストだ。
あなたが自分のパッケージのディレクトリ内でパッケージAに依存しているといった場合、あなたのパッケージがルートパッケージとなる。
パッケージAのディレクトリにcd
したらAがルートパッケージだ。
スタビリティはルートパッケージで決定される。そしてルートパッケージのみで決定される。 これを忘れないようにして、ちょっと考えてみよう。
Composerはユーザの手にわたる依存物がどのようなスタビリティか判断をする。 ユーザとしてあなたは開発版、ベータ、または安定版のリリースを使いたいか決める。
最低限のスタビリティ(minimum-stability)
このスタビリティの判断はルートパッケージのminimum-stabilityフィールドに基づいて行われる。これはルートオンリーだ。スタビリティフラグのデフォルト値を定義し、下限として振る舞う。
これは引き下げることのできるルーラーで、デフォルトは"stable"をさしている。 しかし引き下げると、より低いスタビリティフラグを示すことができる。
minimum-stabilityはすべての制約のためのデフォルトの安定性を定義する。
スタビリティの解決
それでは、つぎのようなシナリオを考えてみよう。ルートパッケージがA:dev- masterを要求していて、 それがさらにB:dev-masterを要求している場合だ。
ルートパッケージは以下のようになる
{ "require": { "A": "dev-master" } }
Composerは以下のステップを踏む:
minimum-stability
を決定:このケースではフィールドが定義されていないのでデフォルト値が設定される。これは"stable"だ。- Aは
dev-master
というバージョン制約をもっている。dev-
プリフィクスがついているので、これはdevバージョンであることがわかる。そしてdevバージョンは"dev"スタビリティをもっている。ルートパッケージで定義されたこのdevバージョン制約のため、暗黙的に@dev
スタビリティフラグを得る。 Aは制約
A:dev-master@dev
をもっているので、このバージョンはマッチしてcomposerはリンクする。AはBにdev-master
という制約つきで依存している。これはdev-
プリフィクスを持っている、よって"dev"スタビリティをもっている。ところが、この制約はパッケージAの中で定義されていて、かつルートパッケージではないので、暗黙的に
@dev
スタビリティフラグを得ることはできない。その代わりにminimum-stability
を継承する。これは"stable"だ。よって、解決される制約はB:dev-master@stable
となる。
ここが障害のポイントだ。なぜならB:dev-master@stable
はどうやっても解決できないからだ。composerは与えられたスタビリティの範囲でパッケージBが見つからないとこたえるのだ。
この問題に対処する方法の一つはminimum-stability
を"dev"に下げることだ。
しかしこれは通常とてもよくないアイデアだ。これはすべての制約に適用されてしまい、
その結果、すべてのパッケージを不安定なバージョンで取得してしまう。
だからお願いだ。それをしないでくれ。
スタビリティフラグ
代わりにスタビリティフラグを使おう。
フラグはバージョン制約の一部として定義される。スタビリティはルートパッケージでのみで決定されるので、フラグもまたルートオンリーだ。 依存パッケージ内で定義されたフラグは単純に無視される。
フラグは不安定版パッケージを指定するホワイトリストとして使うことができる。このケースにおいて、私はBをホワイトリストに追加したい。このようにする:
{ "require": { "A": "dev-master", "B": "@dev" } }
注目すべきなのは、実際のバージョンをルートパッケージ内で定義していない点だ。 これはルートパッケージはインストールされるBのバージョンを扱わないということを意味する。 バージョンの決定は指定する制約をもっているAに委譲している。
これによって、もしAがBへの依存をdev-master
から~1.0
またはそれ以外に変更したとしても、ルートパッケージはなにも変更する必要がなくなる。
Silexの例
この動作が実際にどのようなものか、よりアイデアを得るために、silexを例に見てみよう。
これを書いている現時点で、silexは安定バージョンがない(訳者注:今は1.2の安定バージョンがありますね)。インストールするために@dev
フラグを追加する必要がある:
{ "require": { "silex/silex": "1.0.*@dev" } }
Silexは1.0
の開発バージョンである1.0.x-dev
バージョンのみがある。
Silexのすべての依存物は安定バージョンがある。これはデフォルトでv2.1.7
の多数のsymfonyコンポーネントとv1.0.1
のpimpleを得るということだ。
もし数日前にリリースされたsymfonyコンポーネントのv2.2.0-RC1
バージョンを試したくなった場合、
それを以下のようにホワイトリストにすることができる:
{ "require": { "silex/silex": "1.0.*@dev", "symfony/event-dispatcher": "@RC", "symfony/http-foundation": "@RC", "symfony/http-kernel": "@RC", "symfony/routing": "@RC" } }
バージョンをすべて指定するのは面倒なので、minimum-stability
を下げることもできる。
この場合それはOKだ。あなたが望まない不安定バージョンがインストールされることがないからだ。
{ "minimum-stability": "RC", "require": { "silex/silex": "1.0.*@dev" } }
prefer-stable
このポストを書いてしばらくした後、composerはprefer-stable
という機能をリリースした。
もし、依存物のスタビリティを把握したくない場合、単にprefer-stable
フィールドをルートパッケージで使うことができる。
Composerは可能なもので最も安定した依存物を導きだす。
これはとても便利でほとんどの場合それで十分だ。しかし、私はあなたが本当に必要としているスタビリティを考えることをお勧めする。明示的にそれを定義することもね。あなたは便利さと制御をトレードしてるかもしれない。
結論
composerがどのようにスタビリティを解決しているか。不安定バージョンを取得するためにスタビリティフラグどのように使うことができるか。この記事があなたのよりよい理解に役立てば、幸いだ。
でも覚えておいてくれ:スタビリティフラグを必要とするもっともよくある理由は、あなたの依存物のメンテナが安定板をタグ付けしない理由によるものだ。彼らにブランチエイリアスの追加とタグリリースをさせるため、いってやってくれ。彼らがそれを行ったらすぐに、スタビリティフラグを捨てて、またハッピーになれるよ。