Composerのイケてないところ
例えば、RubyのBundlerだと、カレントディレクトリにGemfileが無くても、プロジェクトルートを勝手に判断して(つまり、Gemfileのあるディレクトリをワーキングディレクトリとして)実行してくれる。
しかし、我らがPHPのComposerはこうである。(以下、/path/to/project
にcomposer.jsonがあると仮定)
$ pwd
/path/to/project/src/hoge
$ composer dumpautoload
Composer could not find a composer.json file in /path/to/project/src/hoge
To initialize a project, please create a composer.json file as described in the https://getcomposer.org/ "Getting Started" section
これの何が困るかというと、例えば composer test
にPHPUnitを設定していて、testsディレクトリ内で作業中にPHPUnitを実行したくなった時、一旦プロジェクトルートにカレントディレクトリを移してコマンドを叩かなければならないことが挙げられる。
一応回避策はある
Global Options
- --working-dir (-d): If specified, use the given directory as working directory.
要するに、
$ pwd
/path/to/project/src/hoge
$ composer dumpautoload --working-dir=/path/to/project
Generating autoload files
こうすればちゃんと動いてくれるということである。が、いちいち指定するのは面倒くさいしアホらしい。
それでこうした
.bashrcに以下の関数を記述した。shファイルに切り出してPATHの通ってるところに置いてもいいかもしれない。
composer() {
local dir="$PWD"
while [ -n "$dir" ]; do
[ -f "$dir/composer.json" ] && break
[ "$dir" = "/" ] && dir="." && break # / の dirname を取ると / が返るので、見つからなかったらカレントディレクトリを使うようにする
dir="$(dirname $dir)"
done
"$(which composer)" "$@" --working-dir="$dir"
}
簡単に言うと、カレントディレクトリから上位ディレクトリに向けてcomposer.jsonを探索し、最初に見つかった場所をワーキングディレクトリとしてcomposerに渡している。
例えば、これで /path/to/project
にいないときでも composer test
するとPHPUnitが実行できるようになる。
もうちょっといい感じにしたかった
これが動かない。
$ pwd
/path/to/project/tests
$ composer test HogeTest.php
> phpunit 'HogeTest.php'
Cannot open file "HogeTest.php".
Script phpunit handling the test event returned with error code 1
composer test tests/HogeTest.php
とプロジェクトルートからのパスを渡してやる必要がある。
もちろん引数をすべてチェックして、パスであれば絶対パスに変換する方法もある。しかし、例えばカレントディレクトリに init
ファイルが存在するとき、composer init
するとうまく動かない。
何かもうちょっといい方法は無いものか・・・
追記 (2017-06-11)
ちょっと手を加えた。
composer() {
local command="$1" && shift
local opt options="" dir="$PWD"
while [ -n "$dir" ]; do
[ -f "$dir/composer.json" ] && break
[ "$dir" = "/" ] && dir="." && break
dir="$(dirname $dir)"
done
for opt in "$@"; do
[ -e "$PWD/$opt" ] && opt="$PWD/$opt"
options+="$opt "
done
"$(which composer)" $command $options --working-dir="$dir"
}
composerの一つ目の引数をコマンドとみなして保管。後の引数をすべて回して、パスであれば絶対パスに変換するという対応をした。前のスクリプトも同様だが、ディレクトリ名にスペースがあるとうまく動かないので注意されたい。また、コマンド呼び出しの $options
をダブルクォートすると、引数を複数渡したときに単一の引数とみなされてうまく動かないので注意。