ComposerでPHPの依存関係を管理する

  • 24
    いいね
  • 0
    コメント

モダンなPHPの依存管理(パッケージ管理)に既に欠かせないものとなったComposerの導入と運用方法について説明します。「仕事で使えるComposer」でもざっくりと紹介しましたが、今回はもうちょっとだけ詳細に書きます。

概要についてざっくりと知りたい型は、先にこちらのスライドをご覧ください。

Composerとは何か

ComposerはPHPのパッケージ(ライブラリやツールなど)をインストールするためのツールです。英語ではComposer is a dependency manager.と説明されます。

ほかのプログラミング言語のエコシステムとして、RubyでのgemコマンドとBundlerを組み合せたもの、Node.jsのnpmに相当します。Composerはこの二つと比べても後発だけあって、それらの良いところが取り入れられたものです。

Packagist

Packagist (The PHP Package Repository)はComposerデフォルトのリポジトリです。Rubyのrubygems.org、PythonのPyPI(Python Package Index)、Node.jsのNPM(npmjs.com)に相当します。

各言語のパッケージマネージャーの動向を定点観測するModule Countsでは先述の各言語のパッケージ数が集計されて居ります。パッケージ数の増加と言語エコシステムの充実には直接の関係はありませんが、PHPにおいてもRubyやPythonに負けない規模の活発な開発コミュニティが育ってることは確かです。

Module Counts 2017-05-14: Packagist(PHP), PyPI(Python) and Rubygems.org

Composerでできること

  • 依存パッケージ(PHPライブラリ)を管理する
    • インストール
    • 更新状態の確認
    • ライセンスの一覧
  • PHPプロジェクトの依存関係を定義する
    • PHPのバージョンとか
    • 必須のPHP拡張とか
  • コマンドラインツールをインストールする
  • クラスの自動ロード(オートローディング)を簡単にする

Composerが可能にすること

従来のPHPではrequire_once("Auth/Auth.php");のようにパッケージに含まれる個別のファイル名を名指しで読む方式でした。

Composerはライブラリを含めた依存性をまとめてインストールしてくれるだけではなく、PHPが本来持つクラスのオートローディングの仕組みを使って、自前のコードとライブラリのコードを自然に統合してくれることにあります。

どのような原理で動作するのかの説明についてはincludeって書きたくない僕たちのためのオートローディングとComposerに書きました。

PEAR

Composerの前の世代のPHPにおけるパッケージ管理ツールはPEARでした。PEARとは何かについてはManual :: What is PEAR?に説明されます。現在のPHPコミュニティにおいては既にあまり重要視されて居りません。

PEARの公式channelは後方互換性を極力維持する方針でしたが、PHP4の時代からあるためE_STRICTレベルのエラーが大量に発生する。PHPには5.3で名前空間(namespace)の機構が取り入れられましたが、

PEARからComposerへの時代に以降した象徴的なできごとは、2014年のPHPUnitのPEARによる提供の終了でした。PHPUnitは2014年4月21日にリリースされた3.7.35および4.0.17をもってPEARを対象とした最終リリースとし、年末をもって専用のPEARチャンネル(pear.phpunit.de)をシャットダウンしました(End of Life for PEAR Installation Method · sebastianbergmann/phpunit Wiki)。

用語の整理

パッケージ (package)

公式のドキュメントでは「何かを含むディレクトリ」A package is essentially just a directory containing something.と説明されます。ComposerはPHPのために設計された依存性マネージャーなので、典型的にはPHPのコードやそれに付随するデータが含まれます。

パッケージは必ず、vendor/name形式の「名前」と「バージョン」を持ちます。私(zonuexe)が作ったマストドンAPIクライアントのパッケージ名は、典型的にはzonuexe/mastodon-apiのようなパッケージ名になります。

ライブラリとしてリポジトリに公開するものだけでなく、Composerで管理されるものはパッケージと呼ばれます。パッケージのタイプにはlibraryprojectがありますが、基本的にはデフォルト(library)で困らないです。

バージョン (version)

バージョンは、よく知られたソフトウェアの更新状況を示す1.2.40.1.12のような数字による表記です。小数点数ではなく、単に.で区切られた数字(あるいは、そのほかの文字)の羅列です。

有名なバージョン付けのガイドラインとしてはセマンティック・バージョニング(semver)があり、このルールにおいては先頭から順に major.minor.patch と呼ばれます。また、正式リリース前のバージョンには1.2.3-pre, 1.2.3-alpha, 1.2.3-beta, 1.2.3-rc.1, 1.2.3-rc.2のような識別子をつけることができます。(ただし、これはバージョンの大小関係においては単純にアルファベットの辞書順に解決されることには注意が必要です)

リポジトリ (repository)

Composerがパッケージを検索するサイトのことです。デフォルトではPackagistが登録され、ここからダウンロードすることができます。

packagist-search.gif

Composer実行ファイルを取得する

Composerは一般的にはコマンドラインのコマンドから利用されます。配布されるファイルの実体としてはPhar形式ですが、利用者はそこまで気にする必要はありません。

Windowsにコマンドをインストールする

別途PHPのインストールは必要ですが、Windows環境でもComposerは動作します。
Composerインストール手順(Windows)を参照してください。

composerコマンドとしてインストールする

はじめにDownload Composerの説明通りに実行します。が、ここまでやってもcomposerをコマンドとしてインストールはされず、 現在のディレクトリにcomposer.pharが置かれるだけ です。

一般的なコマンドラインシェルからは、PATHといふ環境変数に含まれるディレクトリに存在するファイルを実行することができます。環境変数はコマンドラインで echo $PATH | tr ":" "\n" でチェックできます。

% echo $PATH | tr ":" "\n"
/usr/local/sbin
/usr/local/bin
/usr/bin
/usr/local/opt/coreutils/libexec/gnubin
/bin
/usr/sbin
/sbin

つまり、新しいコマンドとしてcomposerを登録するには「A: ファイルを置いたディレクトリをPATHに追加する」「B: PATHに含まれるディレクトリにファイルを置く」のどちらかを決めなければいけません。

A: ファイルを置いたディレクトリをPATHに追加する

ユーザーの実行権限で作成できるディレクトリを一つ決めて、そこに実行ファイルを設置する方法です。筆者は $HOME/local/bin ディレクトリを利用するのが好みです。ほかの実行ファイル置き場を決めてある場合は、改めて作業する必要はありません。

% mkdir -p $HOME/local/bin

% chmod 755 composer.phar
% sudo mv composer.phar /usr/local/bin/composer

# Z Shellの場合
% echo 'PATH=$HOME/local/bin:$PATH' >> ~/.zshrc

# GNU Bashの場合
% echo 'PATH=$HOME/local/bin:$PATH' >> ~/.bashrc

コマンドを実行するためにPATH環境変数にディレクトリを追加することを、俗に「PATHを通す」と呼びます。

B: PATHに含まれるディレクトリにファイルを置く

一般的なUNIXシステムでは、管理者が手動で追加するコマンドは /usr/local/bin に配置される慣習があります。ただし、このディレクトリを操作するにはコンピュータの管理者権限(rootユーザー、あるいはsudoコマンドの実行権限)が必要です。

% chmod 755 composer.phar
% sudo mv composer.phar /usr/local/bin/composer

C: .pharとして利用する

上記のようにコマンドとしてインストールせずとも、phpコマンドを介してComposerを起動することができます。デメリットは、composer.pharファイルがどこにあるのか気を配らなければいけないことです。裏返せば、上記のようにコマンドとしてインストールするメリットとは「作業中にcomposer.pharがどこにあるのかに気を配らなくても良くなる」ことです。

さて、composerをコマンドとしてインストールしない場合、php composer.pharとして実行することができます。

# 現在のディレクトリに composer.phar が存在する場合
% php ./composer.phar

# ホームディレクトリに composer.phar が存在する場合
% php ~/composer.phar

# どこか別の場所 /path/to に composer.phar が存在する場合
% php /path/to/composer.phar

Composerのユースケースを想定したとき、一部の運用者しか直接composerコマンドを利用しない場合は、この方法で十分なことがあります。

実際に筆者が業務で開発するプロジェクトにおいてもこのような運用です。ほとんどの作業者はComposerの存在を意識しなくても、全体のセットアップスクリプト内に含まれるからです。

D: .pharとして利用するラッパーを書く

初カキコ…ども… 俺みたいなAndroidでPHP動かしてる腐れぞぬ野郎、他に、居ますかって居ねーか、はは

残念ながら、上記の「A」「B」どちらの方法でもcomposerをコマンドとして利用できない環境も実際存在します。(具体的には/usr/bin/envが存在しないAndroidなどが該当します)

AndroidのTermuxでPHP+Composerを動かしたい、みたいなひとはラッパースクリプトを書けばいけます。

#!/data/data/com.turmux/files/usr/bin/bash
php $HOME/local/bin/composer.phar "$@"

<details>の中は読まなくてもいいです。

Composerを更新する

Composerは不定期に更新されるので、composer self-updateコマンドで更新されます。

% php composer.phar self-update
Updating to version 1.4.1 (stable channel).
   Downloading (100%)
Use composer self-update --rollback to return to version 1.4.0

特に1.3.0で大きな最適化が入ってますし、バージョンを上げない理由は特にないです。

Composerを高速化する

Composerはパッケージを検索・取得する際にPackagistやGitHubなど大陸をまたいで直列で通信しまくる仕様のため、巨大なパッケージをダウンロードする際は氏ぬほど待たされることがあります(詳しくは @Hiraku光遅い問題を克服してcomposerを10倍速くした話 - Mercari Engineering Blogを読んでください)。

さて、そのblog記事の通りpackagist.jpと並列ダウンロードプラグインのprestissimoを組み合せることで、Composerが効率よく通信できるようになり、環境によっては著しい高速化の効果が得られます。

↓packagist.jpを登録(日本国内の鯖を見に行くと物理的に近い距離で通信するようになって速いよ)

composer config -g repos.packagist composer https://packagist.jp

↓Composerプラグイン(prestissimo; curl拡張使ってるから、並列リクエストしたりKeep-aliveが効いたりの積み重ねで速くなるよ)

composer global require hirak/prestissiomo

通常はそこまで気にしなくていいことではありますが…

同時に大量の鯖にデプロイするような構成の場合はgit pullだけで輻輳するような場合もあるらしいって世界展開する大規模ウェブサービスのデプロイを支える技術 / YAPC::Asia Tokyo 2015 // Speaker Deckで言ってたので、このような場合は愚直にやると逆に性能が劣化する可能性もあるのかもしれません。まあ、そのような場合はどちらにしろくふうが求められますよね><

グローバルにパッケージをインストールする

Composer用語でのglobalとは「特定のプロジェクトに依存しない」程度の意味ですが、実はデフォルトでホームディレクトリに置かれる、ただの一個のプロジェクトです。何を言ってるのかわからないひとは、この説明は気にしなくてもいいです。

気をつけるべきは「グローバルにインストールしたからといってプロジェクトの依存関係として利用できるわけではない」ことです。グローバルと個々のプロジェクトはまったく無関係です。

composerの利用方法として最初に説明するのですが、実のところグローバルに何かをインストールすべきことはあまり多くはありません。

ここでは、PHPのREPL/対話シェル(つまり、RubyのIRBとかPryみたいなやつ)であるPsySHをインストールしてみます。詳しくは、PHPを「シェル化」する [psy/psysh] - 超PHPerになろうとか、WEB+DB PRESS Vol.96に書きました。 (これは商業的宣伝なのでガイドライン違反なのかもしれませんね>< いまから売れても私には一銭も入りませんが)

さて、インストールしてみます。

composer global require psy/psysh
Changed current directory to /Users/megurine/.composer
Using version ^0.8.3 for psy/psysh
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 6 installs, 0 updates, 0 removals
  - Installing jakub-onderka/php-console-color (0.1) Loading from cache
  - Installing jakub-onderka/php-console-highlighter (v0.3.2) Loading from cache
  - Installing dnoegel/php-xdg-base-dir (0.1) Loading from cache
  - Installing nikic/php-parser (v3.0.5) Loading from cache
  - Installing symfony/var-dumper (v3.2.8) Loading from cache
  - Installing psy/psysh (v0.8.3) Loading from cache
symfony/var-dumper suggests installing ext-symfony_debug ()
psy/psysh suggests installing ext-pdo-sqlite (The doc command requires SQLite to work.)
psy/psysh suggests installing hoa/console (A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit.)
Writing lock file
Generating autoload files

最初のChanged current directory to /Users/megurine/.composerが目立ちますね。これは私のmacOSのホームディレクトリなので、ls $HOME/.composerで中身を覗くことができます。

さらにその中にある$HOME/.composer/vendor/bin/を覗いてみますね。

ls -la $HOME/.composer/vendor/bin/

total 44
drwxr-xr-x 13 megurine staff  442 2017-05-10 05:31:36.000 .
drwxr-xr-x 43 megurine staff 1462 2017-05-10 05:31:37.000 ..
lrwxr-xr-x  1 megurine staff   27 2017-05-10 00:53:36.000 apigen -> ../apigen/apigen/bin/apigen
lrwxr-xr-x  1 megurine staff   28 2017-04-27 10:47:00.000 laravel -> ../laravel/installer/laravel
lrwxr-xr-x  1 megurine staff   34 2017-02-11 13:58:07.000 pdepend -> ../pdepend/pdepend/src/bin/pdepend
lrwxr-xr-x  1 megurine staff   17 2017-04-19 00:07:44.000 phan -> ../etsy/phan/phan
lrwxr-xr-x  1 megurine staff   33 2017-05-10 05:31:36.000 php-parse -> ../nikic/php-parser/bin/php-parse
lrwxr-xr-x  1 megurine staff   39 2017-05-10 00:44:45.000 phpcbf -> ../squizlabs/php_codesniffer/bin/phpcbf
lrwxr-xr-x  1 megurine staff   38 2017-05-10 00:44:45.000 phpcs -> ../squizlabs/php_codesniffer/bin/phpcs
lrwxr-xr-x  1 megurine staff   28 2017-02-11 13:58:08.000 phpmd -> ../phpmd/phpmd/src/bin/phpmd
lrwxr-xr-x  1 megurine staff   26 2017-05-10 00:43:48.000 phpunit -> ../phpunit/phpunit/phpunit
lrwxr-xr-x  1 megurine staff   22 2017-05-10 05:31:36.000 psysh -> ../psy/psysh/bin/psysh
lrwxr-xr-x  1 megurine staff   25 2017-04-19 00:07:44.000 tocheckstyle -> ../etsy/phan/tocheckstyle

私はPsySH以外にもいくつもツールを入れてるので量がそれなりにありますが、どれも実行権限がついたシンボリックリンクです。なので、さきほどと同様にPATHを通してやればコマンドが実行できるようになります。

以下のシェルコマンドはBashまたはZ Shell(zsh)が前提です。

# 一時的に追加する場合
% PATH=${COMPOSER_HOME:-$HOME/.composer}/vendor/bin:$PATH

# Z Shellの場合
% echo 'PATH=${COMPOSER_HOME:-$HOME/.composer}/vendor/bin:$PATH' >> ~/.zshrc

# GNU Bashの場合
% echo 'PATH=${COMPOSER_HOME:-$HOME/.composer}/vendor/bin:$PATH' >> ~/.bashrc

シェルでPATH=${COMPOSER_HOME:-$HOME/.composer}/vendor/bin:$PATHを実行してみて、psyshコマンドが動けば大成功です。

あとがき

ねむいなねむいです。

最近は「PHPユーザーズ (日本語)」ってチャットに #composer ってchannelもあって、業務でComposerを運用してるひとたちも集まってる(たぶん)。無料サポートではないですけど、相談に乗ることはできます。きっと。

あとこの記事は、先週ガイドラインとかでわーきゃー騒がしかった時期に書いてました。いま投稿するのは特に理由はなくて、途中で書くのに飽きたのでだらだら書いてたら今までかかったのと、いまも飽きてるので忘れないうちに投稿することにしました。飽きなければ、プロジェクトに導入する方も書きます。