Composerの作者に会った (PHP勉強会 番外編レポート) #phpstudy #composerphp

Posted by Hiraku on 2016-07-23

Composerやmonologの作者である@seldaekが来日するということで、2016年7月22日にPHP勉強会の番外編が開催されました。

PHP勉強会@東京〜番外編〜

動画

@seldaek の発表

発表資料はこちら: Composer in 2016

Composerを使い込んでないとわからない内容もあったと思うので、勝手に補足解説しようと思います。

composerのエコシステムについて

Composerの中央リポジトリ・Packagistの現在のステータスについて。
おおまかなものはPackagist/statisticsで読めますが…

Started April 2011 (5 years!)
~400K visits / month
~200K unique visitors / month
100K packages
2.4 billion installs since April 2012

あとPackagistのログから、Composerユーザーの環境を集計することができます。

例えば、PHPのバージョンを集計するとこんな感じ。

PHP5.6が一番多くて、次がPHP5.5。最近PHP5.5はEOLになったので、まだ使っている人は早くアップデートしましょうね。

一方、登録されたcomposer.jsonのrequire設定に書かれているPHPバージョンは、結構5.3が多い模様。
※ライブラリがサポートしているPHPの最小バージョンを意味します。

5.2   2.51%
5.3   45.26%
5.4   31.69%
5.5   15.48%
5.6   3.52%
7.0   1.54%

45.26%は5.3をサポートしていて、5.3を切り捨てて5.4以降のみ対応しているのが31.69% そろそろ5.3は切っていっても良いかなーという雰囲気を感じます。

Composerのベストプラクティス色々

platformの固定化

composer updateコマンドを打った時、デフォルトでは実行された環境のPHPバージョンを読み取って、依存関係を解決します。 しかし、例えばMacのPHPが5.5で本番環境が5.4だった場合、composer updateしてlockファイルを作った時点では5.5用のライブラリがインストールされてしまい、デプロイしてから動かない!!なんてことになるかもしれません。

そこで、composerには依存関係解決の情報を固定化する手段があります。
composer.jsonにconfig.platformという項目を追加します。

// composer.json
{
  //...
  "config": {
    "platform": {
       "php": "5.6.4",
       "ext-mongo": "1.0.0"
    }
  }
  //...
}

こんな風にすると、composer updateコマンドを実行した環境がどこであろうと、PHP5.6.4向けのライブラリを探してきてくれます。この例だとext-mongoが書いてありますが、そのエクステンションもインストールされているものと見なしてくれます。

ちなみに、config項はエディタで編集しなくても、configというサブコマンドで編集できるので、覚えておくと便利です。

$ composer config -l #現在の設定を全部表示
$ composer config platform.php 5.6.4 #追加 or 上書き
$ composer config --unset platform.php #消す

composer.lockファイルはコミットしましょう

composer.lockはupdateやinstallした後に作られるjsonファイルです。これはバージョン管理システムにコミットするようにしましょう。
updateやlockなしのinstallは、何がインストールされるか保証されません。packagistからパッケージが削除されていて、インストールに失敗する可能性だってあります。
lockファイルがあれば、ユニットテストしたときと同じパッケージが再度インストールされることが保証されます。

静かにインストール

composerは標準でダウンロード状況を表示するので、CIサーバーのログが汚くなることがあります。
--no-progressオプションをつけると、ダウンロードの状況を表示しなくなるので綺麗になります。

デフォルトでは、suggest (あったら便利だよ、推奨だよ)をライブラリが指定している場合、これをログに出力します。不要であれば、--no-suggestで消すことが出来ます。

あと面倒くさければ--quietというオプションもあります。インストールログを全く出力しなくなります。

オートローダーの高速化

PSR-4, PSR-0などのオートローダーは、毎回ファイルパスの実在場所を解決するので若干のオーバーヘッドがあります。--optimize-autoloaderオプションを使うとPSR-4/PSR-0をClassMapとして吐き出すので、オートローダーが高速化されます。

# 好きなコマンドに合わせて付けられる
$ composer update --optimize-autoloader
$ composer install --optimize-autoloader
$ composer dump-autoload --optimize-autoloader

ちなみに省略形として -o でも同様の効果があります。

プロジェクト規模によっては多少時間がかかるので、prod版のパッケージアーカイブを作るときに活用すると良いでしょう。

why (depends) / why-not (prohibits)

最近の新機能 / あまり知られてない機能コーナー。

現在のインストールされているパッケージ一覧はshowコマンドで出せるが、「あれ?なんでこのパッケージ入ってるんだっけ?」と思った時に理由を教えてくれるのがwhy(depends)コマンド。 例えばphpspec/prophecyをrequireした覚えがないのにインストールされているなら、

$ composer why phpspec/prophecy
$ composer depends phpspec/prophecy # こっちでも意味は同じ
phpunit/phpunit  4.8.27  requires  phpspec/prophecy (^1.3.1)

という風に教えてくれます。--tree -tでツリー状に表示してくれます。

$ composer depends phpspec/prophecy -t
phpspec/prophecy v1.6.1 Highly opinionated mocking framework for PHP 5.3+
└──phpunit/phpunit 4.8.26 (requires phpspec/prophecy ^1.3.1)
   └──composer/composer dev-master (requires (for development) phpunit/phpunit ^4.5 || ^5.0.5)

why-not (prohibits) は逆で、なぜそのバージョンがインストールされていないかを教えてくれます。

新しいバージョンが出ていないか調べる

outdatedを使うと、パッケージの新しいものが出ているかを確認することができます。 composer update --dry-run などもあるけど、outdatedだと具体的にどうバージョンが変わるのかの表示がわかりやすいですね。

$ composer outdated

prefer-distオプション

GitHubなどでリポジトリを配信している場合、git cloneでソースを落としてくる(prefer-source)か、zipでダウンロードする(prefer-dist)か選ぶことができます。

通常、prefer-distでダウンロードしたほうが高速です。ただ、アクセストークンなどの認証情報をセットしておく必要があるので、設定できてない人はうまく動かないかもしれません。

そのためか、composerはprivateリポジトリの場合、デフォルトでgit cloneしようとします。

この挙動を変更してzipダウンロードを強制するのが --prefer-dist オプションで、 composer install --prefer-dist などのように使います。

これはパッケージごとにdistかsourceか設定ファイルに書いておくこともできます。

{
    "config": {
        "preferred-install": {
            "nelmio/*": "source",
            "*": "dist"
        }
    },
    "require": {
        "nelmio/foo": "^1.2",
        "other/package": "^1.4"
    }
}

type=pathリポジトリ (mono-repositoryのサポート)

composerのリポジトリはvcsなど色々タイプがありますが、割と最近増えたのが path というリポジトリタイプです。これは該当パスからシンボリックリンクを張るだけというシンプルな機能です。

あるプロジェクトが、最初はsrc/以下に全部のソースコードを突っ込んでいたけど、段々巨大になってしまい、リポジトリを分割したくなったとします。

composerでいきなり別のパッケージに切り出そうとすると、gitリポジトリを別にする必要がありました。せーので大きくソースを移し替えなければならず、大掛かりになりがちでした。

そこでpathリポジトリ。1つのディレクトリの中で複数のcomposerパッケージを仮想的に切り出す機能です。例えばlibというディレクトリを切って、その中にcomposerパッケージとして成立するようなディレクトリを配置し、ルートにもcomposer.jsonを作ります。

  • lib/
    • Date/
      • src/...
      • composer.json # acme/date パッケージとします
    • Hash/
      • src/...
      • composer.json # acme/hash パッケージとします
  • composer.json # requireにacme/dateとacme/hashを入れておきます

この状態のルートでcomposer installすると、vendor/配下にシンボリックリンクを張って、dateやhashのパッケージをインストールしたかのように見せかけてくれます。

十分安定したらlib以下のソースコードを実際にリポジトリ分割すればよく、それまでは1つのリポジトリの中で好きなように開発することが出来ます。

ちなみにtype=pathのリポジトリは、バージョン指定を"@dev"や"dev-master"などにする必要があります。

日本でのレイテンシについて

outdatedコマンドを日本に来てから試したら、びっくりするほど遅かったと言ってくれました!😃 伝わったっぽい!

まさか本人のスライドに載せてもらえるとは! 感動!

質疑応答

Composer自体のバージョンって最近割とアップデートが速いけど、基準はセマンティックバージョニングなの?

=> yes. MAJOR.MINOR.PATCHで、 バグフィックスならPATCHを上げて、 互換性を壊さない機能追加ならMINORを上げて、 大きな変更はMAJORを上げる。

composerのプロジェクトで最も困難だった事は何でしょうか?

=> モチベーションの維持と、ライフワークバランス。

composerの単語の意味は作曲者なのに、ロゴが指揮者なのは何故なの?

=> さほど深い理由はないけど、Google画像検索で調べても、割と指揮者が出てくるし、まあいいんじゃないの。。とのこと

composer.lockをコミットするべし、に対して、monologとかはlockファイルをコミットしてないけど、どうして?

※@seldaekはmonologの作者でもあります

=> ライブラリとして提供する(それ単体でコマンドとして使われることはない)場合、最終的にrequireしたプロジェクト側でcomposer.lockを作るので、それで良いのでは。重要なのは、デプロイされるモノに.lockファイルが明確に定義されていることだと思う。

他の言語でcomposerを再実装するというアイデアはどう思う?

=> 例えば並列処理だけ考えればGoなどで実装した方が綺麗に作れると思うけど、PHPユーザーが「PHPでプラグインを書いて拡張できるようにする」という部分が大きいので、PHPで実装するべきだと思う。

並列リクエストのPullRequestについて

Parallel downloader by hirak ・ Pull Request #5293 ・ composer/composer

英語わからないなりに咀嚼して理解したところとしては、

  • メタデータのダウンロード側に最適化が入ってないのが不満
    • それ入れたらもっとPull Requestが巨大になっちゃうので、あえて外してるんですよ…。
  • 基本的にcomposerはメンテナンスフェーズであって、大きなPull Requestは受け入れづらい
    • +990行は大きい
  • RemoteFilesystemはhackを積み重ねてきた部分で、手を入れづらいし、しっかり互換性を確認しなければならない
  • 数ヶ月以内ぐらいにマージ考えるよ!とのこと

とりあえず、協力は惜しまないようにしたい。

PHPの最新記事