記事のゴール
BEAR.Sundayのチュートリアルでプロジェクト作成時に行う以下の操作。
$ composer create-project bear/skeleton MyVendor.Weekday
Installing bear/skeleton (1.7.4)
- Installing bear/skeleton (1.7.4): Loading from cache
Created project in MyVendor.Weekday
> BEAR\Skeleton\Composer::install
What is the vendor name ?
(MyVendor):
What is the project name ?
(MyProject):Weekday
上記の対話で如何にして名前空間の設定やテストの作成などを行なっているのかを知る。
create-projectの挙動を知る
This is the equivalent of doing a git clone/svn checkout followed by a composer install of the vendors.
---
git clone/svn checkoutのあとにcomposer installを実行すること同等です。
$ composer create-project foo/bar
上記のコマンドは以下の操作と同等のものであるとのこと。
$ git clone https://github.com/foo/bar
$ cd bar
$ composer install
参考:
https://getcomposer.org/doc/03-cli.md#create-project
BEAR\Skeleton\Composer::install について知る
bear/skeleton
インストール直後に呼ばれていたBEAR\Skeleton\Composer::install
がどこから呼ばれていたか探したところ、composer.jsonのscriptsから以下のように呼ばれていることがわかった。
{
...
"scripts" :{
"pre-update-cmd": "BEAR\\Skeleton\\Composer::install",
composer.jsonのscriptsとは何か
A script, in Composer's terms, can either be a PHP callback (defined as a static method) or any command-line executable command.
---
Composerの用語では、スクリプトはPHPコールバック(静的メソッドとして定義)またはコマンドライン実行可能コマンドのいずれかです。
- 静的メソッドとして定義されたコールバック
-
composer xxx
のような形でコマンドラインから実行するコマンド
の2つに分類される。
今回は前者であろう。
参考:
https://getcomposer.org/doc/articles/scripts.md#scripts
pre-update-cmdとは何か
Composerは処理の実行中にいくつかのイベントを発火させる。その中の1つ。
関係しそうなイベントをいくつか抜粋。
イベント | タイミング |
---|---|
pre-install-cmd | composer.lockが存在する状態でinstallコマンドが実行される前 |
post-install-cmd | composer.lockが存在する状態でinstallコマンドが実行された後 |
pre-update-cmd | updateコマンドが実行される前 composer.lockが存在しない状態でinstallコマンドが実行される前 |
post-update-cmd | updateコマンドが実行された後 composer.lockが存在しない状態でinstallコマンドが実行された後 |
post-root-package-install | create-projectコマンド中にルートパッケージがインストールされた後 |
post-create-project-cmd | create-projectコマンドが実行された後 |
イベントは手動で発火させることも可能。
$ composer run-script pre-update-cmd
参考:
https://getcomposer.org/doc/articles/scripts.md#running-scripts-manually
pre-update-cmdはどのタイミングで発火するのか
composer create-project
においてどのタイミングで発火するかテストする。
$ composer create-project piotzkhider/composer:dev-master
Installing piotzkhider/composer (dev-master 2d8512df0a34f4c0b9cd65b08a3feb4721c290b1)
- Installing piotzkhider/composer (dev-master 2d8512d): Cloning 2d8512df0a from cache
Created project in /packages/composer
> Skeleton\Composer::postRootPackageInstall
postRootPackageInstall <- post-root-package-install
> Skeleton\Composer::preUpdate
preUpdate <- pre-update-cmd
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
> Skeleton\Composer::postUpdate
postUpdate <- post-update-cmd
Do you want to remove the existing VCS (.git, .svn..) history? [Y,n]? Y
> Skeleton\Composer::postCreateProject
postCreateProject <- post-create-project-cmd
composer.lock
が存在しないのでinstall系イベントは発火していない。
どのように対話を行なっているのか
When an event is fired, your PHP callback receives as first argument a Composer\EventDispatcher\Event object. This object has a getName() method that lets you retrieve the event name.
---
イベントが発生すると、PHPコールバックは最初の引数としてComposer\EventDispatcher\Eventオブジェクトを受け取り ます。このオブジェクトにはgetName()、イベント名を取得できるメソッドがあります。
コールバックはスクリプトの種類に応じたComposer\EventDispatcher\Event
のサブクラスを引数として受け取ることが出来る。
今回のイベント、pre-update-cmd
はコマンドイベントにあたるためComposer\Script\Eventが渡される。
関係するいくつかのサブクラスを抜粋。他にも色々。
イベントの種類 | 渡されるサブクラス |
---|---|
コマンドイベント | Composer\Script\Event |
インストーラーイベント | Composer\Installer\InstallerEvent |
パッケージイベント | Composer\Installer\PackageEvent |
渡されたEventのサブクラスからIOに関するクラスを呼び出し対話を行なっている。
public function __invoke(Event $event) : void
{
$io = $event->getIO();
$vendor = $this->ask($io, 'What is the vendor name ?', 'MyVendor');
...
private function ask(IOInterface $io, string $question, string $default) : string
{
$ask = sprintf("\n<question>%s</question>\n\n(<comment>%s</comment>):", $question, $default);
return $io->ask($ask, $default);
}
参考:
https://getcomposer.org/doc/articles/scripts.md#event-classes
https://getcomposer.org/doc/articles/scripts.md#defining-scripts
https://getcomposer.org/apidoc/master/Composer/Script/Event.html
名前空間の設定やテストの作成など
素直にスタブに設定された文言を対話で得たベンダ名、パッケージ名へと置き換えていく。
不要となったInstallファイルの削除やディレクトリの権限設定、composer.json
の内容の置換なども同様に難しい手段は取っていない。
private function rename(string $vendor, string $package) : callable
{
$jobRename = function (\SplFileInfo $file) use ($vendor, $package) {
if (is_dir($file) || ! is_writable($file)) {
return;
}
$contents = file_get_contents($file);
$contents = str_replace(
['BEAR.Skeleton', 'BEAR\Skeleton', 'bear/skeleton'],
["{$vendor}.{$package}", "{$vendor}\\{$package}", strtolower("{$vendor}/{$package}")],
$contents
);
file_put_contents($file, $contents);
};
return $jobRename;
}