[Composer] composer.jsonを読み解く | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

[Composer] PHPのパッケージ管理にComposerを使う の続き。

composerの管理に使うcomposer.jsonファイルの中身についての解説です。


composer.jsonの基本フォーマット


前回のエントリ内で書いたcomposer.jsonは下記の内容でした。


{
  "require": {
    "squizlabs/php_codesniffer": "dev-master",
    "pear/pear_exception": "1.0.*@dev",
    "pear/log": "dev-master"
  }
}

この中で、requireキーにはComposerで管理する対象パッケージとバージョンを「:」で区切り(というかJSONの配列フォーマット)で記述します。

Packagistを見ながらインストールする場合、requireキーの書き方は記載があるので参照しながら書いていくのがよいと思います。


特定のバージョンをインストールしたい場合は、バージョン欄に番号やブランチ名などを指定します。

バージョンの番号はワイルドカードやレンジで指定することもできます。

「1.0.*」とした場合、1.0系の全てのバージョン(1.0.1や1.0.2など)が対象となります。

「1.*」とした場合、1系の全てのバージョン(1.1や1.2、1.1.1や1.1.10など)が対象となります。


下記のようにレンジを指定した場合、その範囲のバージョンが対象となります。


{
  "require": {
    "squizlabs/php_codesniffer": "1.0 - 2.0",
    "pear/pear_exception": ">=1.0,<1.1"
  }
}

squizlabs/php_codesnifferのレンジの指定の仕方では、バージョン1.0から2.0まで(1.0や2.0を含む)が対象となります(つまり、1.1や1.1.10、1.9など)。

pear/pear_exceptionの場合は、バージョン1.0から1.1まで(1.1を含まない)が対象となります(つまり、1.0.1や1.0.10など)。

結構細かい制御ができるみたいで、「~1.2」と書くと1.2以上、2.0未満を対象にしたりといろんな書き方が用意されているみたいなので詳細はマニュアル も参照してください。


もう1つ最初のcomposer.jsonの例の中で


"pear/pear_exception":1.0.*@dev,

という書き方がありますが、最後の@devはスタビリティフラグと呼ばれるものらしいです。

これはそのパッケージの安定性を示すフラグで、デフォルトではstable(安定版)となっています。

ちょっとパッケージの例を変えますが、nette/utils というパッケージの最新版をインストールするには


"nette/utils":2.3.*@dev

という書き方をしますが、スタビリティフラグ(@dev)を削除して


"nette/utils": "2.3.*"

のような書き方をすると「2.3.*@stable」という扱いになるのでパッケージが見つからないというエラーが出てしまいます。


$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - The requested package nette/utils could not be found in any version, there may be a typo in the package name.

Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your minimum-stability setting
   see  for more details.

Read  for further common problems.

最初の設定例のpear/exceptionでも同様なのですが、こちらには@dev以外にも安定板のバージョン1.0.0が公開されているため、エラー無くインストールできたりします。

この場合、@devの1.0.x-devのバージョンではなくstableのバージョン1.0.0がインストールされてるわけですが、この2つはエイリアスになっているので結局は一緒のものがインストールされてます。


デフォルトのスタビリティフラグを変えたい場合、minimum-stabilityという属性をセットすることで変更することができます。


{
  "minimum-stability": "dev",
  "require": {
    "nette/utils": "2.3.*"
  }
}

上記の例であればうまくインストールができますが、パッケージ全体のスタビリティフラグが変わるため、安定しないバージョンのパッケージがインストールされる可能性があり、通常は変更せずにパッケージごとに必要に応じてスタビリティフラグを明記したほうがよいと思います。

この辺の話は、下記の記事が詳しく書かれています。


Composerがパッケージのstabilityを解決するしくみ @ オープンソースこねこね



その他の便利なオプションとか


依存関係を無視してインストールする


composer.jsonに設定できるその他の便利なオプションとして、依存関係を無視するprovideオプションがあります。

例えば、pear/logパッケージはpear/pear_exceptionパッケージを必要としますが、下記の様に書くことでpear/pear_exceptionをインストールしなくてもpear/logパッケージを導入できます(それで動くか動かないかは別にして)。


{
  "provide": {
    "pear/pear_exception": "*"
  },
  "require": {
    "pear/log": "dev-master"
  }
}

ソースだけダウンロードしたい場合や依存関係にあるもののその機能をまったく使わないといった場合は便利なオプションかもしれません。


リポジトリを指定する


デフォルトでcomposerがパッケージを検索、取得するリポジトリはpackagistからになりますが、別のリポジトリを指定することもできます。

例えば、PEARパッケージはpackagistでも提供されていますが、PEARのリポジトリからもインストール可能です。


{
  "repositories": [
    {
      "type": "pear",
      "url": "http://pear.php.net/"
    }
  ],
  "require": {
    "pear-pear.php.net/PEAR" : "*"
  }
}

上記のようにrepositoriesの属性を追加します。


$ composer install
Loading composer repositories with package information
Initializing PEAR repository http://pear.php.net
Installing dependencies (including require-dev)
  - Installing pear-pear.php.net/xml_util (1.2.3)
    Downloading: 100%         
  - Installing pear-pear.php.net/console_getopt (1.3.1)
    Downloading: 100%         
  - Installing pear-pear.php.net/structures_graph (1.0.4)
    Downloading: 100%         
  - Installing pear-pear.php.net/archive_tar (1.3.13)
    Downloading: 100%         
  - Installing pear-pear.php.net/pear (1.9.5)
    Downloading: 100%         
Writing lock file
Generating autoload files

上記のようにPEARサイトからインストールされていることがわかります。 packagistからインストールした場合の表示はこんな感じ。


$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing pear/pear_exception (v1.0.0)
    Loading from cache

  - Installing pear/xml_util (1.2.3)
    Downloading: 100%         

  - Installing pear/console_getopt (v1.3.1)
    Downloading: 100%         

  - Installing pear/structures_graph (dev-trunk 6331691)
    Cloning 633169128f282e749b1efdbda77b2760a8b110d0

  - Installing pear/archive_tar (1.3.11)
    Downloading: 100%         

  - Installing pear/pear (dev-master d346efa)
    Cloning d346efa9809bdf0e53d711fbffd385d070207f96

pear/pear suggests installing pear/pear_frontend_gtk (For GTK support)
pear/pear suggests installing pear/pear_frontend_web (For Web support)
Writing lock file
Generating autoload files

ちなみに、repositoriesの属性はその他のリポジトリからもソースをダウンロードできたりして、例えばcomposerを通してjQueryをインストールする(インストールというかソースのダウンロード)というようなこともできたりします。


{
  "repositories": [
    {
      "type": "pear",
      "url": "http://pear.php.net/"
    },
    {
      "type": "package",
      "package": {
        "name": "jquery/jquery",
        "version": "2.1.3",
        "type": "jquery",
        "dist": {
          "url": "http://code.jquery.com/jquery-2.1.3.js",
          "type": "file"
        }
      }
    }
  ],
  "require": {
    "pear-pear.php.net/PEAR" : "*",
    "jquery/jquery": "*"
  }
}

上記のようにpackageのタイプを追加し、ダウンロード先のURLなどの情報を書いておき、requireでjqueryを指定すればインストールできます。


$ composer update
Loading composer repositories with package information
Initializing PEAR repository http://pear.php.net
Updating dependencies (including require-dev)
  - Installing jquery/jquery (2.1.3)
    Downloading: 100%         
Writing lock file
Generating autoload files

まぁ、使い道の良し悪しはあるもののパッケージ管理という観点では楽になるかもしれません。

詳細はマニュアル も参照してください。


インストール前後でスクリプトを実行する


インストールやアップデートをトリガーとしてとあるバッチを動かしたいといったことも可能です。

例えば、パッケージが更新されたらメール通知を行うといったことができます。


{
  "autoload": {
    "psr-0": {
        "MyVendor\\MyClass": ""
    }
  },
  "scripts": {
      "post-package-install": [
          "MyVendor\\MyClass::postPackageInstall"
      ],
      "post-package-update": [
          "MyVendor\\MyClass::postPackageInstall"
      ]
  },
  "require": {
    "nette/utils": "2.3.*@dev"
  }
}

scriptsの属性が設定箇所で、post-package-installやpost-package-updateがトリガーとなるイベント名です。

それぞれ、パッケージがインストール/更新が発生した際にMyClass.phpのpostPackageInstall()を呼び出すという処理になります。

設定できるトリガーはマニュアル 参照。


MyClass.phpは下記のようなものです。


<?php

namespace MyVendor;

use Composer\Script\Event;

class MyClass
{
    public static function postPackageInstall(Event $event)
    {
        $installedPackage = $event->getOperation()->getPackage()->getName();
        var_dump($installedPackage);
    }
}


このファイルは、Composerのルートディレクトリ内(composer.jsonが配置されているのと同じディレクトリ)のMyVendor/MyClass.phpとして保存しています。

パスの通し方は、Composerがオートロードできる箇所ならどこでもよいのですが、composerのautoloadの設定にてパスを指定していればそのプログラムが呼び出されます。


余談ですが、インストール/更新されたパッケージ名を取り出すことをしているのですが、$event->getOperation()->getPackage()はパッケージがインストールされた場合しか呼び出せません。

ですので、post-update-cmdやpost-install-cmdのイベントで呼び出そうとすると下記のようにPHPエラーが発生します。


Fatal error: Call to undefined method Composer\Script\CommandEvent::getOperation() 

実際にcomposerのスクリプト内を見ればわかりますが、post-update-cmdではCommandEventクラスが呼び出され、post-package-updateはPackageEventクラスが呼び出されているからです。

後はスクリプト次第なので、運用・保守で必要なロジックを書いていけばよいかと思います。