昨今のPHPを使った開発の現場では、パッケージ(プロジェクトで利用するライブラリ)の依存管理をComposerに任せて、packagistに登録されたOSSのパッケージや、Satis等で構築したプライベートなComposerリポジトリに登録した自社のパッケージを組み合わせて開発するスタイルになっていると思います。 このような開発で、自社のパッケージも積極的に作っていく場合、どうしても、1つのアプリケーションを開発しながら同時に複数のライブラリパッケージを修正するといったことが必要になってきます。
Composerを使うと、開発対象のアプリケーションの依存パッケージは、プロジェクトのvendorディレクトリ配下にすべて保存され、composer update
コマンドで更新などが行えます。複数のパッケージの開発を同時に進める場合は、基本的にルートとなる1つのアプリケーション/パッケージのプロジェクトを用意して、そのvendor配下に展開された個々のパッケージという関係で作業します。つまり、vendor配下に開発中のパッケージも入っている状態です。このような状態で、vendor配下に入っているパッケージもその場で更新などを行っていけると、動作する1つのアプリケーションで一気通貫に開発できてとてもスムーズになります。
今回と次回との2回に分けて、Composerを使った複数パッケージの同時開発のTIPSを解説します。今回はまず基礎知識として、vendor配下をComposerがどのように管理しているのかということを整理しておきます。
vendor配下のパッケージのgitの状態
普通にstableリリースされているパッケージをcomposer require
すると、VCS系の情報が削除された状態でインストールされます。GitHubのリポジトリでリリースしているパッケージであれば、リリースタグごとのzipファイル等から展開されます。たとえばquartet/contextual-validator
をcomposer require
してみると、次のようになります。
$ composer require quartet/contextual-validator
Using version ^1.1 for quartet/contextual-validator
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing symfony/property-access (v2.7.0)
Downloading: 100%
- Installing phpmentors/domain-kata (v1.3.0)
Downloading: 100%
- Installing quartet/contextual-validator (v1.1.6)
Downloading: 100%
Writing lock file
Generating autoload files
ここでvendor/quartet/contextual-validator
ディレクトリへ移動してみると、そこに.git
ディレクトリはないことが分かります。
├── .gitignore
├── .php_cs
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
├── src
└── tests
Composerによるパッケージのインストール時にVCSの情報を保持したままにしたい場合は、コマンドのオプションに --prefer-source
をつけます。この指定は、OSSへパッチを送りたいときなどに便利です(実際の開発で使う指定は、次回解説します)。
$ composer require quartet/contextual-validator --prefer-source
Using version ^1.1 for quartet/contextual-validator
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing symfony/property-access (v2.7.0)
Cloning bc738f091816455d5c30b99f5a8d714469b44ad4
- Installing phpmentors/domain-kata (v1.3.0)
Cloning 0395da95eb2c8d88bccd1cffb0f371242bc69809
- Installing quartet/contextual-validator (v1.1.6)
Cloning cfe034f88a563955134ca7dd165807dc9a7dea6f
Writing lock file
Generating autoload files
メッセージでもリポジトリをcloneしていることが分かります。今度は.git
ディレクトリがある状態になりました。
├── .git
├── .gitignore
├── .php_cs
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
├── src
└── tests
.git
ディレクトリがありますので、このディレクトリ配下を独立してgit操作可能です。
このパッケージのgitローカルリポジトリは、Composerで取得したときに次のような状態になっています。
- remote名
composer
が登録される(URLは、ソースの取得元) - 該当するコミットがチェックアウトされ、デタッチ状態になる (prefer-sourceの場合)
- (gitのdetached HEADについては西尾さんのブログ記事などご参考に)
git remoteの結果は次のようになっています
$ git remote -v
composer git://github.com/quartetcom/contextual-validator.git (fetch)
composer git://github.com/quartetcom/contextual-validator.git (push)
origin git://github.com/quartetcom/contextual-validator.git (fetch)
origin git@github.com:quartetcom/contextual-validator.git (push)
また、tig等でログ/ブランチすると、次のようになっています。
Composerのソースでは
Composer/Downloader/GitDownloader.php
のupdateToCommit()
メソッドで処理を行っています。
このあたりであれこれやっているのは、次で説明するような「ローカルに変更があった場合」のさまざまな対応をしているということかと思います。
vendor配下のコードに変更がある場合
vendor配下のパッケージがgitリポジトリになっている場合、開発中にそこのファイルを直接編集したら、その後のパッケージの更新などはどうなるのでしょうか? ためしに、vendor/quartet/contextual-validatorディレクトリでREADME.mdの内容を少し編集しておきます(コミットなどは行いません)。
$ git status
HEAD detached at v1.1.6
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: README.md
この状態で、プロジェクトのルートディレクトリへ移動して、composer status
コマンドを実行すると、変更が検出されます。
$ composer status -v
You have changes in the following dependencies:
(....)/vendor/quartet/contextual-validator:
M README.md
(この変更の検出には、内部でgit status --porcelain --untracked-files=no
というコマンドが実行されています)
この状態でcomposer update quartet/contextual-validator
などとした場合、Composerリポジトリ(packagist)上のcontexual-validator本体に更新がなければ特に何も起こらず、READMEの変更もそのままになります。
では、contextual-validator本体が更新されていたらどうでしょうか?この場合、ローカルの変更をどうするのか、プロンプトが表示されます。
$ composer update quartet/contextual-validator
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Updating quartet/contextual-validator dev-master (86ddd81 => 79342b3)
The package has modified files:
M README.md
Discard changes [y,n,v,s,?]?
y
と入力すれば、ローカルの変更は破棄され、composer updateが継続されます。変更をどこかにとっておきたい場合は、s
を入力すると変更がgit stashされcomposer updateが継続され、その後stashされた分の復帰を試みます。stashの復帰が問題なく完了すればcomposer updateの処理が継続しますが、競合などがあってstashの復帰に失敗した場合は、composer updateが中断されます。プロンプトの時点でn
を入力すれば、中断になります。
Composerのソースでは
Composer/Downloader/GitDownloader.php
に次のメソッドがあります。
discardChanges()
メソッド- 内部では
git reset --hard
が実行されています
- 内部では
stashChanges()
メソッド- 内部では
git stash
が実行されています
- 内部では
reapplyChanges()
メソッド- 内部では
git stash pop
が実行されています
- 内部では
まとめ
Composerは便利なツールですが、Composerへの依存度が高くなってくると、バージョンの指定方法やコマンドの挙動などについて理解を深めておかないと、いろいろと悩まされることが増えてきます。 また、Composerの挙動と合わせて、gitに代表されるバージョン管理ツールの使い方の知識も補強しておくと、ぐっと安心感が高まりますね。
次回は、composer.jsonでのバージョン指定方法との絡み、開発の流れなどを解説します。