Composerの実践的な使い方とトラブル対処

※本ページには広告(アフィリエイトプログラム等)が含まれます。

LaravelにしろSymfonyにしろ、PHPを触る以上Composerとは長い付き合いになる。ただ composer install と composer update の違いを曖昧にしたまま本番でコマンドを打つと、思わぬ形でバージョンが変わって事故る。ここではさくらVPS上でのデプロイを前提に、実践でつまずきやすいポイントをまとめる。

install と update の決定的な違い

この2つはよく混同されるが、動作原理がまったく違う。

  • composer install: composer.lock に書かれた正確なバージョンをそのままインストールする。依存解決はしない。
  • composer update: composer.json のバージョン制約を見て依存関係を解決し直し、composer.lock を新しく書き換える。

つまり本番サーバーで「動かない」と思って安易に composer update を叩くと、ローカルで検証していない新しいマイナー・パッチバージョンが一気に降ってくる。composer.lock があるのに update を実行するのは、ロックの意味を自分で捨てる行為だと思ったほうがいい。開発中に依存を最新化したい時だけ update を使い、デプロイ時は必ず install、が基本ルール。

composer.lock は必ずgitにコミットする

.gitignore に composer.lock を入れているプロジェクトを時々見るが、これはやめたほうがいい。lockファイルが無いと、本番で composer install を叩いた瞬間に依存解決が走ってしまい(実質 update と同じ状態)、開発環境と本番で違うバージョンのパッケージが入る可能性が出る。「ローカルでは動くのに本番だけ壊れる」の典型パターンの一つ。composer.lock はアプリケーションコードと同じくらい重要な成果物なので、必ずコミットする。

本番デプロイの基本コマンド

さくらVPSでのデプロイ時は次の形で実行する。

composer install --no-dev --optimize-autoloader

--no-dev でテストツールやデバッグ用パッケージ(phpunit等)を除外し、本番に不要な依存を持ち込まない。--optimize-autoloader でクラスマップを事前生成し、オートロードの解決を高速化する。開発中に --no-dev を付けたままだと artisan test 等が動かなくなるので、開発機では付けない。

require のバージョン制約: ^1.2 と ~1.2 の違い

パッケージ追加は次の形。

composer require guzzlehttp/guzzle
composer require --dev phpunit/phpunit

バージョン制約の記号は地味に間違えやすい。

  • ^1.2: メジャーバージョンが変わらない範囲で最新まで許容(1.2.0 〜 2.0.0未満)。事実上のComposerの標準的な書き方。
  • ~1.2: 最後の桁だけ変動を許容(1.2.0 〜 1.3.0未満)。~1.2.3 なら1.2.3〜1.3.0未満と、指定した桁の位置で範囲が変わる点に注意。

^ の方が広い範囲を許容するので通常はこちらを使う。セキュリティ修正だけを厳密に追いたいライブラリに限って ~ を使う、くらいの使い分けでいい。

オートロード再生成

クラスの追加・削除・namespace変更をした直後に「クラスが見つからない」と言われたら、まずオートロードを再生成する。

composer dump-autoload -o

-o(--optimize)を付けるとクラスマップ方式で生成され本番同様の速度で確認できる。vendor/ を消していないのに原因不明のクラス未検出エラーが出た時は、まずこれを疑う。

メモリ不足エラーへの対処

小さいVPS(メモリ1GBクラス)で composer update や大きめのパッケージの require を叩くと、依存解決の途中でメモリを使い切って落ちることがある。

COMPOSER_MEMORY_LIMIT=-1 composer update

または

php -d memory_limit=-1 /usr/local/bin/composer update

のようにmemory_limitを無制限にして回避する。メモリが本当に足りていないVPSでは、一時的にswapを増やしてから実行するのも手。ただしswapを常用するのは推奨しない、あくまで一時的な回避策。

プラットフォーム要件エラーと –ignore-platform-reqs

「required PHP extension ext-xxx is missing」のようなエラーが出ると、つい --ignore-platform-reqs を付けて黙らせたくなるが、これは安易に使わないほうがいい。実際に拡張が入っていない、あるいはPHPバージョンが足りていない状態のまま強引にインストールするだけなので、実行時に別のエラーとして跳ね返ってくる。

拡張が本当に不要と分かっている、あるいはCI環境だけの制約なら、composer.json 側で明示的に宣言したほうが健全。

{
    "config": {
        "platform": {
            "php": "8.3.0"
        }
    }
}

こうしておけば、どのPHPバージョンを前提にしているかがコードとして残り、チームの他メンバーやCIでも同じ判断基準になる。

グローバルインストール

プロジェクトに依存しないCLIツール(例: laravel/installer)は global require で入れる。

composer global require laravel/installer

実行ファイルにPATHが通っていないと「コマンドが見つからない」となるので、~/.bashrc 等に以下を追加しておく。

export PATH="$PATH:$HOME/.config/composer/vendor/bin"

(パスはOSやComposerのバージョンによって ~/.composer/vendor/bin の場合もあるので、環境による。)

壊れた時の定番リセット

依存関係がぐちゃぐちゃになって訳が分からなくなった時の最終手段。

rm -rf vendor composer.lock
composer install

ただしこれは composer.lock ごと消すので、composer.json の制約次第では今まで動いていたバージョンから変わる可能性がある(実質 update と同じことが起きる)。本番でいきなりやるコマンドではなく、ローカルで依存関係の不整合を切り分ける時の手段と考えたほうがいい。本番で試す前に、ステージング環境かローカルで一度動作確認してから反映する。

キャッシュのクリア

パッケージのバージョンを上げたはずなのに古い挙動のまま、という時はComposerのキャッシュを疑う。

composer clear-cache

これで ~/.cache/composer 配下のダウンロード済みパッケージ・メタデータキャッシュが消える。次回の install / update は多少遅くなるが、キャッシュ由来の不整合を疑う時にまず試す価値はある。

まとめ

Composer周りのトラブルは大半が「install と update の使い分け」を守れば防げる。本番は常に composer.lock に忠実な install、依存を動かすのはローカルでの update。この原則さえ徹底しておけば、あとはメモリやプラットフォーム要件のような個別のエラーに個別に対処するだけで済む。

読んで頂いて有り難うございます!