LoginSignup
5
4

More than 3 years have passed since last update.

Composerのcreate-projectは何をしているのか、何が出来るのか

Last updated at Posted at 2019-08-12

記事のゴール

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から以下のように呼ばれていることがわかった。

composer.json
{
    ...
    "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に関するクラスを呼び出し対話を行なっている。

src/Install.php
    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の内容の置換なども同様に難しい手段は取っていない。

src/Install.php
    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;
    }
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4