Composer bin スクリプトの作り方
composer をインストールすると、パッケージに含まれる CLI ベースの補助ツールのシンボリックリンクが、自動的に vendor/bin に作られます。
この補助ツールを自分で作る方法について、いろいろと罠にハマったので記録しておきます。
composer.json の編集
composer.json の bin プロパティに補助ツールのパスを記述します。パスはプロジェクトルートからの相対パスとします。
- composer.json
{ "bin": [ "path/to/bin", "path/to/another", ... ] }
autoloader の解決
- composer に記述した補助ツールは呼び出しのための bootstrap とし、メイン処理は別ファイルに記述.
- bootstrap の中では、autoloader の呼び出し、メイン処理の呼び出しのみを記述した方が良い.
- autoloader の呼び出しは、自パッケージがどこから呼ばれる可能性があるかを意識し、複数の階層から呼ばれる構造にしておく.
- vendor/bin/script はシンボリックリンクになっており、シンボリックリンクで呼び出されたスクリプトは実ファイルのパスを基準に実行される.
- つまり、composer package としてインストールされた場合、$PRJ_ROOT/vendor/company/package/bin/script が呼び出される.
- このため、ロードする autoload.php のパスは
__DIR__ . '/../../../autoload.php'
となり、vendor を含めると
__DIR__ . '/../../../../vendor/autoload.php'
となる.
- 参考
- メイン処理の呼び出しは、既に autoloader を呼み込み済みであるため、クラスを直接呼出し可能.
#!/usr/bin/env php <?php $autoloaders = [ __DIR__ . '/../vendor/autoload.php', // 自プロジェクトからの呼び出し __DIR__ . '/../../../../vendor/autoload.php' // composer インストールされた先からの呼び出し ]; foreach ($autoloaders as $file) { if (file_exists($file)) { define('AUTOLOADER_PATH', $file); break; } } unset($file); require AUTOLOADER_PATH; // Call Main Script Some\Cli\Application::main();
ファイルパスの解決
autoloader と同様、コマンドの引数にパスを渡すような場合、実ファイルのパスをベースに解決しようとすると、自身の存在するパスが変化するため、実装が難しい。
このため、getcwd() という関数を使用することでパスの解決を行う。
Phinx では Symfony\Component\Config\FileLocator というパッケージを使って設定ファイルのパスを解決をしている。
https://github.com/cakephp/phinx/blob/master/src/Phinx/Console/Command/AbstractCommand.php
https://github.com/symfony/config/blob/5.3/FileLocator.php
なお、vendor/bin にはシンボリックリンクが作られるが、シンボリックリンクから実行しても、実行した場所のディレクトリ(シンボリックリンクの親ディレクトリ)を得られるため、気にせず実装できる。