背景
レガシーなサイトを PHP 8.0 にアップグレードする際、非推奨(deprecation)警告への対応は、まるで「モグラ叩き」のように感じられることがよくあります。その中でも特に頻繁に遭遇するのが、"Required parameter follows optional parameter" という警告です。PHP 7.4 以前のバージョンでは、曖昧な関数のシグネチャも許容されていましたが、現在のエンジンはより厳密な規律を求めています。この変更は単に厳しくなっただけではなく、言語内に長年存在していた論理的な落とし穴を解消するためのものです。
エラーメッセージ
開発中、エラーログやページ上に直接以下のような警告が表示されることがあります:
Deprecated: Required parameter $config follows optional parameter $options in /var/www/html/app.php on line 42
原因分析
オプション引数にはデフォルト値がありますが、必須引数にはありません。古いコードベースでよく見られる、問題のあるシグネチャの例を見てみましょう:
function processData($options = [], $data) {
// ここにロジックを記述
}
ここでの論理は破綻しています。$data に文字列を渡すためには、物理的にまず $options に何らかの値を指定せざるを得ません。もし processData('my data') と呼び出そうとすると、PHP 7 は誤って文字列を $options に割り当て、その後に $data が不足しているとエラーを出します。最初の引数が「オプション」であるというのは嘘だったわけです。実際にはスキップすることができなかったのです。PHP 8.0 では、すべての必須パラメーターを列の先頭に配置することを義務付けることで、この混乱を解消しています。
ステップバイステップの修正方法
1. 関数の定義場所を特定する
ログに記載されているファイルと行番号を確認してください。デフォルト値(= [] や = null など)を持つ変数が、デフォルト値のない変数の前に記述されている関数やメソッドが見つかるはずです。
// 問題のあるシグネチャ
function initializeApp($options = ['debug' => true], $config) {
$timeout = $config['timeout'];
}
2. 引数の順序を入れ替える
必須の変数を左側に移動します。これが最もクリーンな修正方法であり、他の開発者にとっても関数の意図がすぐに明確になります。
// 修正後のシグネチャ
function initializeApp($config, $options = ['debug' => true]) {
$timeout = $config['timeout'];
}
3. 呼び出し元を同期させる
シグネチャを変更すると、その関数への既存のすべての呼び出しが壊れます。initializeApp が使用されている場所を見つけ、引数を入れ替える必要があります。PhpStorm のような最新の IDE であれば、「シグネチャの変更(Change Signature)」ツールを使用して、100以上のファイルにわたるリファクタリングを数秒で処理できます。
// 以前の方法(現在はエラーまたは論理的なバグを引き起こす)
initializeApp(['debug' => false], ['timeout' => 30]);
// 修正後の方法
initializeApp(['timeout' => 30], ['debug' => false]);
4. 「APIセーフ」な代替案:Null許容型
厳密なインターフェースに従っている場合や公開ライブラリを作成している場合など、引数の順序を変更できないことがあります。そのような場合は、オプション引数を必須としつつ、Null許容(nullable)にします。これにより、引数の順序を変えずに警告を消すことができます。
// Null許容型を使用して順序を維持する
function initializeApp(?array $options, $config) {
$options = $options ?? ['debug' => true];
}
この場合、呼び出し側はデフォルト設定を使用したい場合でも、明示的に null を渡さなければならない点に注意してください。
PHP 8 の名前付き引数の活用
引数の順序を整理した後は、PHP 8 の名前付き引数機能を使うことでコードの可読性が大幅に向上します。リスト内の位置を気にすることなく、オプション引数を完全にスキップできるようになります。
function createUser($name, $role = 'guest', $status = 'active') {
// ...
}
// $role をスキップして直接 $status を指定する
createUser(name: 'Jane Doe', status: 'pending');
検証手順
リファクタリング直後に、PHP の lint コマンドを使用して構文エラーがないか確認します:
php -l /var/www/html/app.php
構文が有効であれば、アプリを操作しながらリアルタイムでログを監視します。Linux では、tail -f /var/log/php-fpm.log を実行して、Deprecated 警告が消えたことを確認できます。Docker を使用している場合は、docker logs -f [container_name] でコンテナのログを確認してください。
学んだ教訓
- **必須引数を先に:** 「オプション引数の前に必須引数を置く」ことを鉄則としてください。Python、JavaScript、Java などで標準となっているのには理由があります。
- **PHP 9 への備え:** 8.x で「非推奨(Deprecated)」とされている挙動の多くは、PHP 9.0 で致命的な「エラー(Error)」例外になります。将来の自分が週末を潰してストレスを抱えないよう、今のうちに修正しておきましょう。
- **静的解析は不可欠:** PHPStan などのツールをレベル5以上で使用しましょう。これらのシグネチャの不一致を、本番環境のログに出る前に CI/CD プロセスで見つけ出してくれます。

