0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Composer updateが通らない本番でTwigだけを2.12から2.15へ差し替えた手順

0
Posted at

環境情報

  • EC-CUBE 4.0.x
  • PHP 7.4.x
  • Composer 2.8.x
  • Twig 2.12.x → 2.15.x
  • Webサーバー: Apache 2.4 + PHP-FPM
  • OS: Amazon Linux 2
  • 本番制約: composer update を実行できない。脆弱性対応を最優先。

EC-CUBE 4.0の本番環境で、Twigを脆弱性対応のために2.12から2.15へ上げる必要がありました。しかし本番のComposer 2.8は、composer.json 内に大文字を含むパッケージ名があると受け付けません。結果として composer update が走らず、脆弱性を抱えたまま稼働が続く状態でした。

そこで本番全体の依存解決は行わず、vendor/twig/twig だけを公式タグのzipで差し替え、composer.lock と vendor/composer/installed.json の該当箇所だけ版と参照を書き換える方法を選びました。追加クラスは1件だけで、TwigはPSR-4で読まれるため、今回の環境ではautoload再生成は不要でした。切り戻しも退避した旧ディレクトリを戻すだけです。

起きたこと

composer update を実行すると、依存解決の前に composer.json のバリデーションで落ちます。エラーは次のようなものです。

Invalid package name "EC-CUBE/...": Package name must be lowercase.

Composerのパッケージ名は小文字が必須です。Composer 2.8ではこの検証が厳しくなり、大文字を含むパッケージ名が composer.json にあるだけでupdateが拒否されます。EC-CUBE本体やプラグイン側で大文字名が残っていると、Twig単体の更新もできません。

この状態で放置すると、Twigの脆弱性修正が入らないまま本番が動き続けます。一方で、本番で composer update を実行して依存全体を動かすのは、PHPバージョン制約、プラグイン互換、DBマイグレーション、ダウンタイムの観点でリスクが大きいです。

取った方針

今回は「環境全体を直す」のではなく「Twigだけ差し替える」を選びました。

  • vendor/twig/twig を公式タグのzipで置換
  • composer.lock の twig/twig ブロックを更新
  • vendor/composer/installed.json と installed.php の版情報を更新
  • 追加クラスは1件のみ。PSR-4で名前空間と配置が対応するため、autoload再生成は不要
  • 切り戻しは退避した旧ディレクトリとバックアップしたlock/installedを戻すだけ

比較表

アプローチ メリット デメリット 使う条件
composer update で全体更新 依存関係が正しく解決される 本番影響大。プラグイン互換やPHP制約で失敗しやすい ステージングで検証済み
composer require twig/twig:^2.15 依存解決を任せられる composer.json の大文字名検証で同じく落ちる可能性 根本修正済み
vendor/twig/twig を手動差し替え 最短で脆弱性対応できる lockと実ファイルが一時的に不整合 緊急対応。切り戻し可能
WAFやミドルウェアで緩和 アプリを触らない 根本修正にならない 時間稼ぎ

手順

0. バックアップ

まずlockとvendorを退避します。本番では必ず復元できる状態にします。

cd /path/to/ec-cube
cp -a composer.lock composer.lock.bak.$(date +%Y%m%d%H%M%S)
cp -a vendor/composer/installed.json vendor/composer/installed.json.bak.$(date +%Y%m%d%H%M%S)
cp -a vendor/composer/installed.php vendor/composer/installed.php.bak.$(date +%Y%m%d%H%M%S)
cp -a vendor/twig/twig vendor/twig/twig.old.$(date +%Y%m%d%H%M%S)

1. 現状確認

現在のTwigバージョンと参照を確認します。

php -v
composer --version
composer show twig/twig
grep -n '"name": "twig/twig"' composer.lock
grep -n '"name": "twig/twig"' vendor/composer/installed.json

composer show が大文字名の検証で落ちる場合は、composer.lock と installed.json を直接確認します。

2. Twig 2.15のzip取得

公式リポジトリのタグzipを取得します。コミットハッシュはGitHub APIから取ります。

curl -s https://api.github.com/repos/twigphp/Twig/git/refs/tags/v2.15.0 | jq -r '.object.sha'

取得したハッシュを使ってzipを展開します。

curl -L -o /tmp/twig-2.15.0.zip \
  https://github.com/twigphp/Twig/archive/refs/tags/v2.15.0.zip
unzip -q /tmp/twig-2.15.0.zip -d /tmp/twig-2.15.0
ls /tmp/twig-2.15.0

3. vendor/twig/twigを差し替え

旧ディレクトリはすでに退避済みなので、新しいTwigを配置します。

rm -rf vendor/twig/twig
mv /tmp/twig-2.15.0/Twig-2.15.0 vendor/twig/twig
ls vendor/twig/twig/src

4. composer.lockを更新

composer.lock の twig/twig ブロックだけを更新します。version、version_normalized、source.reference、dist.reference、dist.url を新しい値に合わせます。

{
  "name": "twig/twig",
  "version": "v2.15.0",
  "version_normalized": "2.15.0.0",
  "source": {
    "type": "git",
    "url": "https://github.com/twigphp/Twig.git",
    "reference": "NEW_COMMIT_HASH"
  },
  "dist": {
    "type": "zip",
    "url": "https://api.github.com/repos/twigphp/Twig/zipball/NEW_COMMIT_HASH",
    "reference": "NEW_COMMIT_HASH",
    "shasum": ""
  }
}

content-hash は composer.json を変更していないため原則そのままです。ただし手動編集後は composer validate が通るか確認します。

5. installed.json / installed.phpを更新

Composer 2では vendor/composer/installed.json と vendor/composer/installed.php の両方に版情報が入ります。実行時に Composer\InstalledVersions を参照するライブラリがあるため、両方を更新します。

{
  "name": "twig/twig",
  "version": "v2.15.0",
  "version_normalized": "2.15.0.0",
  "reference": "NEW_COMMIT_HASH"
}

installed.php は次のような配列です。

'twig/twig' => array(
    'pretty_version' => 'v2.15.0',
    'version' => '2.15.0.0',
    'reference' => 'NEW_COMMIT_HASH',
    'type' => 'library',
    'install_path' => __DIR__ . '/../twig/twig',
    'aliases' => array(),
    'dev_requirement' => false,
),

6. OPcacheクリアとPHP-FPMリロード

PHPファイルを差し替えただけではOPcacheが古いコードを返すことがあります。

php -i | grep opcache.enable
sudo systemctl reload php-fpm

必要に応じてApacheやNginxもリロードします。

7. 動作確認

TwigのバージョンとComposerの認識を確認します。

php -r "require 'vendor/autoload.php'; echo \Twig\Environment::VERSION, PHP_EOL;"
php -r "require 'vendor/autoload.php'; echo \Composer\InstalledVersions::getPrettyVersion('twig/twig'), PHP_EOL;"
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/

テンプレート描画、管理画面、フロント画面、キャッシュ生成を一通り確認します。

8. 切り戻し

問題があれば退避ディレクトリとバックアップを戻します。

rm -rf vendor/twig/twig
mv vendor/twig/twig.old.20250101000000 vendor/twig/twig
cp -a composer.lock.bak.20250101000000 composer.lock
cp -a vendor/composer/installed.json.bak.20250101000000 vendor/composer/installed.json
cp -a vendor/composer/installed.php.bak.20250101000000 vendor/composer/installed.php
sudo systemctl reload php-fpm

ハマりポイント

  • Composer 2.8の大文字パッケージ名拒否は、composer update だけでなく composer require や composer dump-autoload でも発動する可能性があります。
  • composer dump-autoload が通らない場合、classmap-authoritativeが有効だと新しいクラスを読めません。今回の環境はPSR-4フォールバックが有効だったため不要でした。
  • installed.php を忘れると、Composer\InstalledVersions::getVersion() が古い値を返します。
  • OPcacheをクリアしないと、差し替え後も古いTwigが動いているように見えます。
  • composer install を後で実行すると、手動編集したlockと実ファイルが上書きされる可能性があります。

FAQ

Q. composer install は使えますか?
A. Composer 2.8が composer.json を拒否する場合、installでも同じ検証に落ちる可能性があります。今回は手動差し替えを前提にしました。

Q. autoload再生成は不要ですか?
A. PSR-4で新規クラスが読まれるなら不要です。ただし classmap-authoritative が有効な場合は必要です。composer dump-autoload が通るなら実行してください。

Q. lockとinstalled.jsonのどちらを直すべきですか?
A. 両方です。lockは将来の composer install、installed.jsonとinstalled.phpは実行時メタデータだからです。

Q. 根本解決は?
A. composer.json 内の大文字パッケージ名を小文字に修正し、composer validate --strict を通すことです。

composer validate --strict
composer update --dry-run

学び

次からは、更新が通らないときに「lockと実ファイルのどちらを正とするか」を先に決めます。

  • lockを正とするなら、手動差し替えは一時避難。後で必ず依存解決をやり直す。
  • 実ファイルを正とするなら、lockとinstalledを合わせて更新し、次のデプロイで戻らないようにする。

この判断を先に決めないと、緊急対応したはずの差分が次のデプロイで消えたり、逆に不整合を起こしたりします。更新が詰まったとき、あなたは環境を直す派ですか。一部だけ差し替える派ですか。


この記事を書いた人

BENTEN Web Works — 業務自動化・システム開発のフリーランスエンジニアです。

GAS / Python / RPA を使った業務自動化や、Web制作・システム開発のご相談を承っています。
「こんなこと自動化できる?」というご質問だけでもお気軽にどうぞ。

👉 BENTEN Web Works — 詳細・お問い合わせはこちら
🐦 X(旧Twitter) — 日々の知見を発信中

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?