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

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

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

[![](https://qiita-image-store.s3.amazonaws.com/0/4121/ccb9922e-acf0-026b-06b7-b76b9531f914.png)](http://niconare.nicovideo.jp/watch/kn1371)

## Composerとは何か

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

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

### Packagist

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

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

![Module Counts 2017-05-14: Packagist(PHP), PyPI(Python) and Rubygems.org](https://qiita-image-store.s3.amazonaws.com/0/4121/7a81e582-9d98-f2ab-ee45-b3ed088f3da8.png)


## Composerでできること

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

## Composerが可能にすること

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

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

どのような原理で動作するのかの説明については[includeって書きたくない僕たちのためのオートローディングとComposer](http://qiita.com/tadsan/items/a78c9160418200e2d47a)に書きました。

### PEAR

Composerの前の世代のPHPにおけるパッケージ管理ツールは[PEAR](https://ja.wikipedia.org/wiki/PEAR)でした。PEARとは何かについては[Manual :: What is PEAR?](http://pear.php.net/manual/en/about.pear.php)に説明されます。現在の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](https://github.com/sebastianbergmann/phpunit/wiki/End-of-Life-for-PEAR-Installation-Method))。

## 用語の整理

### パッケージ (package)

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

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

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

### バージョン (version)

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

有名なバージョン付けのガイドラインとしては[セマンティック・バージョニング(semver)](http://semver.org/lang/ja/)があり、このルールにおいては先頭から順に `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](https://packagist.org/)が登録され、ここからダウンロードすることができます。

![packagist-search.gif](https://qiita-image-store.s3.amazonaws.com/0/4121/ce415082-cfea-3ef4-1cf8-b2b9eb0efa6d.gif)




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

Composerは一般的にはコマンドラインのコマンドから利用されます。配布されるファイルの実体としては[Phar](http://php.net/manual/ja/intro.phar.php)形式ですが、利用者はそこまで気にする必要はありません。

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

別途PHPのインストールは必要ですが、Windows環境でもComposerは動作します。
[Composerインストール手順(Windows)](http://qiita.com/mikoski01/items/266469535e860312145d)を参照してください。

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

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

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

```sh
% 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` ディレクトリを利用するのが好みです。ほかの実行ファイル置き場を決めてある場合は、改めて作業する必要はありません。

```sh
% 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`コマンドの実行権限)が必要です。

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

#### C: `.phar`として利用する

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

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

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

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

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

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

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

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

<details><summary><em>初カキコ…ども… 俺みたいなAndroidでPHP動かしてる腐れぞぬ野郎、他に、居ますかって居ねーか、はは</em></summary><p>残念ながら、上記の「A」「B」どちらの方法でも<code>composer</code>をコマンドとして利用できない環境も実際存在します。(具体的には<code>/usr/bin/env</code>が存在しないAndroidなどが該当します)</p><p>Androidの<a href="https://termux.com/">Termux</a>でPHP+Composerを動かしたい、みたいなひとはラッパースクリプトを書けばいけます。</p><pre><code>#!/data/data/com.turmux/files/usr/bin/bash
php $HOME/local/bin/composer.phar "$@"
</code></pre></details>

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

## Composerを更新する

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

<details><summary><code>% php composer.phar self-update</code></summary><pre><code>Updating to version 1.4.1 (stable channel).
   Downloading (100%)
Use composer self-update --rollback to return to version 1.4.0
</code></pre></details>

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

## Composerを高速化する

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

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

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

<a href="https://packagist.jp/"><img width="666" alt="composer config -g repos.packagist composer https://packagist.jp" src="https://qiita-image-store.s3.amazonaws.com/0/4121/8b351273-0e4e-0716-7c82-cd0794ec05ca.png"></a>

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

<a href="https://github.com/hirak/prestissimo"><img width="413" alt="composer global require hirak/prestissiomo" src="https://qiita-image-store.s3.amazonaws.com/0/4121/b1e88c4e-eedc-196c-0530-83614a6c407f.png"></a>

<details><summary>通常はそこまで気にしなくていいことではありますが…</summary><p>同時に大量の鯖にデプロイするような構成の場合は`git pull`だけで輻輳するような場合もあるらしいって[世界展開する大規模ウェブサービスのデプロイを支える技術 / YAPC::Asia Tokyo 2015 // Speaker Deck](https://speakerdeck.com/hatena/yapc-asia-tokyo-2015)で言ってたので、このような場合は愚直にやると逆に性能が劣化する可能性もあるのかもしれません。まあ、そのような場合はどちらにしろくふうが求められますよね＞＜</p></details>

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

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

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

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

ここでは、PHPのREPL/対話シェル(つまり、RubyのIRBとかPryみたいなやつ)である[PsySH](http://psysh.org/)をインストールしてみます。詳しくは、[PHPを「シェル化」する [psy/psysh] - 超PHPerになろう](http://cho-phper.hateblo.jp/entry/2015/11/10/031000)とか、[WEB+DB PRESS Vol.96](http://gihyo.jp/magazine/wdpress/archive/2017/vol96)に書きました。 *(これは商業的宣伝なのでガイドライン違反なのかもしれませんね＞＜ いまから売れても私には一銭も入りませんが)*

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

<details><summary><code>composer global require psy/psysh</code></summary><pre><code>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</code><pre></details>

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

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

<details><summary><code>ls -la $HOME/.composer/vendor/bin/</code></summary><pre><code>
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</code></pre></details>

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

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

```sh
# 一時的に追加する場合
% 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ユーザーズ (日本語)](http://www.msng.info/archives/2016/02/phpusers-ja-on-slack.php)」ってチャットに `#composer` ってchannelもあって、業務でComposerを運用してるひとたちも集まってる(たぶん)。無料サポートではないですけど、相談に乗ることはできます。きっと。

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