症状
Drupal core を 11.3 系から 11.4 へ上げようとしたときの話です。composer update が1本の contrib モジュールで止まりました。ファイルストレージを S3 互換のオブジェクトストレージへ向けている S3 File System(drupal/s3fs)です。
このモジュールは外せません。サイトのファイル保存の根幹が乗っています。しかも調べると、コード自体は 11.4 でも動きそうでした。作者がまだ 11.4 を検証していないので上限を切っている、という状態です。
第1の関門: Composer の依存解決
この状況の定番の回避策が mglaman/composer-drupal-lenient です。指定したパッケージの core 制約だけを緩め、Composer の依存解決を通します。
{
"require": {
"mglaman/composer-drupal-lenient": "^2"
},
"extra": {
"drupal-lenient": {
"allowed-list": ["drupal/s3fs"]
}
}
}
allowed-list は必ず対象を1本に絞るべきです。ここを広げると、本当に非互換なモジュールが混ざったまま core が上がってしまい、何が原因で壊れているのか分からなくなります。
これで composer update は通り、core 11.4 が入りました。
実際にコードが動かない部分があった
ここで終わりではありませんでした。キャッシュを再構築した時点で、サービスコンテナのコンパイルが失敗します。
ArgumentCountError(s3fs.mime_type.guesserサービス)Missing proxy classのコンパイラ警告
core 11.4 が MIME タイプ推定の仕組みを破壊的に変更していたためです。11.4 の MimeTypeGuesser はこうなっています。
// core/lib/Drupal/Core/File/MimeType/MimeTypeGuesser.php
public function __construct(
protected StreamWrapperManagerInterface $streamWrapperManager,
protected \Traversable $guessers,
) {}
変更点は2つです。
1. 収集メソッドが消えた。 従来は addMimeTypeGuesser() / addGuesser() を呼んで推定器を後から登録する方式でした。11.4 ではコンストラクタ第2引数に tagged iterator で注入する方式に変わり、収集メソッドそのものが無くなりました。s3fs のコンパイラパスは収集メソッドを呼んでいたので、第2引数が渡らず ArgumentCountError になります。
2. ProxyClass が同梱されなくなった。 そのため lazy: true を指定できません。core 自身の file.mime_type.guesser も非 lazy になっています。
対応は2つのパッチです。1つ目は、11.4 以上のときだけ tagged iterator を注入して lazy を切る形にします。古い core でも動くよう、バージョン分岐を入れておくのが要点です。パッチを当てた状態で core を戻す可能性があるので、片方向にしない。
// s3fs のコンパイラパスに追記
if (version_compare(\Drupal::VERSION, '11.4', '>=')) {
$consumer->setArgument(1, new TaggedIteratorArgument($tag));
$consumer->setLazy(FALSE);
return;
}
// 11.4 未満は従来の収集メソッド方式をそのまま通す
2つ目は services.yml から lazy: true を落とすだけです。
s3fs.mime_type.guesser:
class: Drupal\Core\File\MimeType\MimeTypeGuesser
arguments: [ '@stream_wrapper_manager' ]
- lazy: true
これでコンテナは通り、実際のファイル I/O・MIME 推定・画像スタイル派生の生成も動くようになりました。
第2の関門: Drupal はまだ「非互換」と言っていた
コードは動いています。それなのに、管理画面のステータスレポートは s3fs を**「互換性のないモジュール」**として表示し続けました。drush updatedb も要件エラーのプロンプトを出します。
ここが本題です。
composer-drupal-lenient が緩めるのは Composer の依存解決だけです。 Drupal 自身のモジュール互換判定は、Composer のメタデータを見ていません。モジュールの .info.yml にある core_version_requirement を実行時に読んでいます。
# s3fs.info.yml
core_version_requirement: ">=8.8 < 10.7 || >=11.0 < 11.2 || >=11.2.3 < 11.4"
上限が < 11.4。だから 11.4.x は範囲外で、非互換と判定されます。composer-drupal-lenient はこの行に何もしません。管轄が違うのです。
つまり互換チェックは二重にあります。
| 関門 | 判定に使うもの | 緩める手段 |
|---|---|---|
| Composer の依存解決 | パッケージの composer.json | composer-drupal-lenient |
| Drupal のランタイム判定(status / updb) | モジュールの .info.yml の core_version_requirement | .info.yml へのパッチ |
有名な回避策は片方しか面倒を見てくれません。.info.yml 側もパッチで上限を広げます。
name: S3 File System
-core_version_requirement: ">=8.8 < 10.7 || >=11.0 < 11.2 || >=11.2.3 < 11.4"
+core_version_requirement: ">=8.8 < 10.7 || >=11.0 < 11.2 || >=11.2.3 < 12"
これで非互換表示も updatedb のプロンプトも消えます。
なお、この順序には意味があります。.info.yml を広げるのは、コードが実際に動くことを確認した後にすべきです。先に広げると、Drupal が出してくれていた唯一の警告を自分で黙らせた状態で、壊れたコードを本番へ運ぶことになります。
おまけの罠: パッチが当たらない
パッチを書いて composer install しても、当たらないことがあります。
cweagans/composer-patches の v1 系は、パッケージのバージョンが変わっていないと新しいパッチを再適用しません。すでに展開済みのモジュールに対して、パッチ定義を1本足しただけでは何も起きません。
対象を消して入れ直す必要があります。
rm -rf web/modules/contrib/s3fs
composer install
「パッチファイルは書いた、composer.json にも登録した、なのに .info.yml が変わっていない」でしばらく悩みました。差分が当たっているかどうかは、composer の出力を信じずに対象ファイルを直接見て確認するのが確実です。
まとめ
- Drupal の互換チェックは二重にある。 Composer の依存解決(composer.json)と、Drupal ランタイムの判定(
.info.ymlのcore_version_requirement)は別系統 composer-drupal-lenientは Composer 側だけを緩める。composer updateが通っても、ステータスレポートとupdatedbは.info.ymlを読んで「非互換」と言い続けるallowed-listは対象を1本に絞る。 広げると本当に非互換なモジュールを取り込んでも気づけない- Composer が通ることと、コードが動くことは別。 core 11.4 は
MimeTypeGuesserの収集メソッドを廃して tagged iterator のコンストラクタ注入へ移し、ProxyClass も廃止した(ArgumentCountErrorとMissing proxy classの原因) - contrib へのパッチはバージョン分岐を入れて version-neutral に書く。 core を戻す可能性があるので片方向にしない
.info.ymlの上限を広げるのは、実機でコードが動くと確認した後。 先に黙らせると、唯一の警告を自分で消した状態で本番へ運ぶcweagans/composer-patchesv1 はパッケージのバージョンが変わらないと新パッチを再適用しない。rm -rfしてcomposer installする。当たったかは対象ファイルを直接見て確認する
この記事は、事業・Webサイトの売買プラットフォーム RIKKA M&A を開発する中で得た知見です。RIKKA M&A は GitHub リポジトリを解析する技術デューデリジェンスを備え、Drupal 11 / PHP / LLM で構築・運用しています。

