PHP界で流行っているパッケージ依存管理ツールComposerですが、使っているでしょうか。似たようなツールはだんだん淘汰されてComposerに一本化され、PEARより盛り上がっている感じです。
がしかし。個人的に使った感触では、表題の通り、検索が遅くてたまらんです。(前からそうだったと思うんですが、一向に改善されないばかりか悪化しているのでは?)
実行するマシンのスペックに依存しますが、手元にあるVMPlayer上で動かしているしょぼいFreeBSDだと、composer search phpunit
するだけで2分ほど動きが止まります。PackagistのWeb UI上で検索すると一瞬で結果が返ってくるのに。。
Composerにはcomposer init
という、composer.jsonを対話的に作るコマンドがありますが、使いたいライブラリを一つ検索するたびに数分待たされるので、非常にストレスフルです。検索をちょっとでも使うような行為はすべて遅く、composer install
もcomposer.lockファイルがない限り遅いです。もう何なの。
なぜ遅いのか
Composerはライブラリの依存関係を解決するためのツールです。加えて中央集権的な独自のリポジトリも持っていて、それがPackagistです。誰でも審査なしでパッケージを登録でき、その手軽さがComposerの盛り上がりに拍車をかけています。
Composerに何も設定せずにパッケージを検索すると、このPackagistから検索しようとします。これが非常に遅いのです。
と言っても、packagist.orgのサイトが遅いというわけではありません。gzipも対応しているし、必要なファイルはダウンロードした後でキャッシュされるようになっているし、サイト自身に問題はないと思います。
問題は検索のアルゴリズムで、どうも斜め読みした限りだと、Packagistに登録されているすべてのパッケージ情報を含んだ巨大なJSON定義ファイルをメモリ上に全展開し、それを一つ一つstrpos()で文字列比較して検索しているようなのです。
…そりゃあ遅くもなるわ。
Packagistは先述の通り審査等は不要で誰でも手軽にパッケージを登録できるので、個人が試しにアップしたようなHelloWorld的クソパッケージがたくさん登録されています。また、バージョン違いのパッケージも全てJSONに含まれており、こいつらを逐次走査してたら時間もかかろうものです。
せめてパッケージ名と概要だけのインデックスを作ってその中だけで検索するとか、そもそも全文検索はSQLiteに任せるとか、いろいろ改善はできると思うのですが。。もともとpackagist.orgがこんなに中央集権的に盛り上がるとは思っていなかったんでしょうか。。
packagist.orgにはどんどんパッケージが登録されていくし、既存パッケージもどんどんバージョンが増えていくし、このままだとpackagist.orgおよびComposerは破滅するのではないかと心配です。(中の人はどうする気なんだろう?)
自衛手段
Composerはpackagist.orgを使わないように設定することもできます。あまり建設的な策ではありませんが、packagist.orgからよく使うパッケージだけ抽出して小さなオレオレPackagistを作り、その中だけで検索するようにすれば、かなりスピードアップするはずです。
オレオレpackagistを作る方法もComposerの公式ドキュメントに書かれています。packagist.orgのサイト自身がOSSとして開発されているのでそれをクローンするか、satisという静的リポジトリを作るライブラリを使うか、その2択のようです。ここでは手軽そうなsatisを使います。
satisは静的にindex.htmlおよびpackages.jsonを作るだけのライブラリで、JSONを作った後はPHPを必要としません。ビルドしたindex.htmlとpackages.jsonだけ置くことができればいいので、昔懐かしのYahoo!ジオシティーズとか、DropBoxのpublicディレクトリでも動くんじゃないだろうか(試してないけど)
まず、satisをGithubからcloneしてきて、composer installして依存ライブラリを取ってきます。composer.lockがアップされているのですんなりインストールできるはず。
#(適当な作業ディレクトリにて) $ git clone https://github.com/composer/satis.git $ cd satis $ composer install
ここからビルドに必要なsatis.jsonを作ります。
リポジトリにpackagist.orgを登録し、その中から含めたいパッケージ名をrequireに列挙していくだけです。
以下は例ですが、homepageやnameは適当に埋めてください。
{ "name": "Packagist popular" ,"homepage": "http://packagist.tojiru.net" ,"repositories": [ {"type":"composer", "url":"https://packagist.org"} ] ,"require": { "composer/composer": "*" ,"seld/jsonlint": "*" ,"composer/satis": "*" ,"monolog/monolog": "*" ,"justinrainbow/json-schema": "*" ,"twig/twig": "*" ,"twig/extensions": "*" ,"symfony/symfony": "*" ,"symfony/console": "*" ,"symfony/event-dispatcher": "*" ,"symfony/process": "*" ,"symfony/routing": "*" ,"symfony/translation": "*" ,"symfony/finder": "*" ,"symfony/config": "*" ... } }
このsatis.jsonを使ってビルドを走らせます。これまた時間がかかりますが、我慢して待ちます。
$ bin/satis build path/to/satis.json web/
終わったら、web/配下にindex.htmlとpackages.jsonの二つのファイルが出来上がっているはずです。後はこの二つをウェブサーバーで公開するだけ。以下はお試しで作ってみたものです。
Packagist popular Composer Repositoryこれでオレオレpackagistができあがったので、本家packagist.orgの代わりにこれを見るように設定を書きます。-gもしくは--globalで$HOME/.composer/config.jsonに設定値を書き込みます。
$ composer config -g repositories.packagist composer http://packagist.tojiru.net
↓config.jsonがこんな感じで吐かれるはず。
{ "config": { }, "repositories": { "packagist": { "type": "composer", "url": "http://packagist.tojiru.net" } } }
この状態でcomposer search phpunit
すると、4秒で検索できるようになりました。これならそれほどストレスも感じないレベルでしょう。
注意点
satisは作ったら終わりなので、ライブラリのアップデートに追随したいなら定期的に再ビルドして、packages.jsonを更新しなくてはなりません。crontabなどで自動化するといいかもしれません。
また、satisは依存関係にあるパッケージを再帰的には見てくれないようです。例えば、requireにphpunit/phpunitだけ記載すると、本当にphpunitのみpackages.jsonに含まれた状態になってしまい、依存パッケージが含まれず、インストール不能になってしまいます。packagist.tojiru.netではひとまず手動で依存パッケージを書き並べましたが、自動化したいところ。
まとめ
- composer searchは遅い。
- オレオレpackagistで改善できるけど、こういう不毛な努力は疲れるので抜本的解決をしたいところ。
あ、ちなみにpackagist.tojiru.netは勝手に使っても構いませんが、特に更新頻度とかメンテナンスとか保証する気はありませんので、ちゃんとしたのが欲しければ自分でビルドするようにしてくださいね。
keyword: COMPOSER