2024年3月12日alt-s
コラム

PHP8へのアップデートで発生する未定義変数( Warning: Undefined variable )エラーの対応策

WebサイトやWebアプリケーションの開発において、その敷居の低さからPHPは現在でも有効な選択肢です。シンプルな記述が出来るため、簡単なものではメールフォームの実装などで活用しているケースも多いことでしょう。

2024年1月現在、PHPは市場シェア76.5%(※1)を誇り、随時バグ・セキュリティの改善、年毎のマイナーバージョンアップで機能強化を続けています。
※1 https://w3techs.com/(W3Techs調べ)

バージョンアップ自体は歓迎すべきことですが、当然、バージョンアップを境に変更となる機能、仕様も出てきます。
変更の中には下位互換性のないものも存在し、PHPが7から8に変わった際、未定義の変数をPHPのコード中で使用した場合は「Warning: Undefined variable」エラーを返す、という大きな変更が行われました。

動的型付け言語だったPHP

PHPはプログラミング言語のひとつであり、PythonやRubyのようなスクリプト言語に分類され、「動的型付け言語」という特徴を持ちます。
「動的型付け言語」は未定義の変数を許容します。プログラムの途中でいきなり変数「a」というデータの入れ物が現れても、もともと在ったものとして動いてくれます。

PHP

正しい記述1
$a = 0; // 変数の宣言 ※2
$a = $a + 1; // 変数の使用
正しい記述2
$a=$a+1; // 突然変数を使用
正しい記述3
$a = 0; // 値は数値でも ※3
$a = “あ”; // 文字でもOK

※2 変数の宣言はPHP7までは無くても問題ありませんし、記述しても問題ありません。
※3 動的型付け言語では変数のデータの種類を問いません。
数値を入れれば数値、文字を入れれば文字として扱ってくれます。
その他、文字であっても、計算式の中にある時は数字として扱ってくれるなど、とにかく融通をきかせてくれます。

$a = 3;  //変数a は数値の3
$b = “5”;  //変数b は文字としての”5”
$c = $a + $b; //変数c は数値の8、変数b の文字を数値として扱ったうえで計算してくれる

この仕様はhtmlのフォームなど、どんな値が入力されるか分からないWebの環境と相性が良い、というメリットがありました。
しかしその反面プログラムが大きくなると、実際にどんな種類(文字か数値かなど)のデータが入ってくるかは動かしてみないと分からなくなってくる、というデメリットもありました。
なお、C言語などの高級プログラミング言語では、データを格納する「変数」を使いたい場合、使う前にプログラム中であらかじめ「使いますよ」と「宣言(定義)」する必要があります。

C言語など

正しい記述
int a = 0; // 変数の宣言
a = a + 1; // 変数の使用
エラーになる記述1
a = a + 1; // 突然変数を使用
エラーになる記述2
int a = 0; // 整数で宣言 ※4
a = “あ”; // 値が整数でない

※4:C言語では変数の型に一致しないデータの種類は認められません。

C言語では、どんな種類(上記の場合整数値しか入らない)のデータが入っているかが、プログラムを見るだけで分かるというメリットがある反面、融通が利かないため、フォームから入ってくる値を厳格にチェックし、整数以外を弾かないとエラーが発生してプログラム自体が止まってしまうというデメリットがあります。
そして、PHP8でのこの仕様変更は、PHPでの「正しい記述2」が、C言語の「エラーになる記述1」なるよう、変更されたということになります。
障害時の対応やメンテナンス性を考えていくと、最終的にはC言語のような仕様になっていかざるを得ないため、以下に
PHP8にバージョンアップしたら、「Warning: Undefined variable」エラーが出るようになってしまったPHPへの対応策を2つご紹介します。

対応策1:機械的にPHPの先頭で変数の定義を行う

シンプルに、変数を定義(宣言)するよう記述を追加して対策します。
可読性を考えて、処理の途中に変数定義を書くことは避け、PHPの先頭部分に書きます。
関数定義等では各関数ブロックの先頭部分に変数定義を書くようにします。

PHPファイル

※PHP内で使用している変数の定義を先頭に集めることで、何の変数が使われているか一目瞭然となります。

関数定義のPHPファイル

※関数の場合も同様に関数のブロックの先頭に変数の定義を記載します。こうすることで可読性が上がります。
なお、フォーム等から渡ってくるPOSTデータが未定義だった場合、PHPでそれを参照してしまうと「Warning: Undefined variable」エラーが発生してしまいます。

POSTデータの場合、単純に変数定義をしてしまうと、渡ってきた値を上書きで消してしまうことになるため、以下のようにisset()で判定して、未定義の場合だけ変数定義するように記述します。

※5 $_POST[‘(任意の項目名)’]が未定義の場合のみ、$_POST[‘(任意の項目名)’]の初期値を空文字で定義することになります。
$_POST[‘(任意の項目名)’]が存在する場合はそのままの値が残ります。

ひとつひとつの変数に対して、都度if文を記述すると、記述行数が増えて可読性も落ちていくため、代わりに3項演算を使用することをお勧めします。
上記のif文は3項演算で以下のように書き換えることができます。

$_POST[‘action’] = isset($_POST[‘action’])?($_POST[‘action’]):(“”);
※POSTデータに直に値を格納することはあまり行儀が良いことではありませんので、通常は適切な変数への代入としてください。

その他、フォームでは1点注意すべきことがあります。
ラジオボタンなどの選択によって非表示となる項目などがあった場合、非表示中だった項目はsubmit後のPOSTデータに含まれません。
そのため、項目がフォーム上にあるので未定義となることはない、と思っていると選択肢によってエラーになったりならなかったり、という調査に時間のかかる不具合を招きます。

フォームの構造
名前:[      ]
住所:[      ]
種類:○チョコレート ●クッキー
クッキー:
□バタークッキー
□抹茶クッキー
□アーモンドクッキー
※種類でクッキー選択中のためチョコレートの項目が非表示中
PHP7までは有効な記述

※1:非表示になっているチョコレート項目( $_POST[‘choco_list’] )が存在しない(未定義な)ため、PHP8ではこの位置でエラーになります。

上記のPHPも、以下のように一律、機械的に修正すれば問題ありません。

PHP8に対応した記述

※1:非表示になっているチョコレート項目( $_POST[‘choco_list’] )が存在しない(未定義な)ため、$choco には空の配列(array())が入ります。

いかがでしょうか。
PHPの先頭に変数名の列挙がずらっと並ぶのは違和感があるかもしれませんが、C言語などでは当たり前に行われていることですので、ぜひ慣れて頂き、見やすいPHPプログラムにしていきましょう。

対応策2:エラーの表示を抑制する

「Warning: Undefined variable」エラーは分類としては「Warning(警告)」にあたるため、エラーメッセージは表示されるものの、PHP自体は動作を続けます。
そのため、エラーの表示を出さないようにして運用を続けるという対応も可能です。
ただ、PHP7では「Notice(注意)」の分類だった「Undefined variable」エラーが、PHP8では「Warning(警告)」に格上げされていることもあり、将来的には動作しなくなることが予想されます。
以下は、あくまでその場しのぎの対応となりますので、恒久対応としてのお勧めは致しません。緊急性がある場合にのみご検討いただければ幸いです。

PHPごとに抑制を指定(各PHPの先頭に記述)

単純にエラーメッセージをWebブラウザの画面に出さないようにします。
.htaccessに以下を追記
php_value display_errors Off
.htaccessの置かれたディレクトリ配下のPHPが影響をうけます。
set_error_handler() を使用して、エラーをトラップする
https://www.php.net/manual/ja/function.set-error-handler.php
独自のエラーハンドラを定義し、エラーを無視して処理を続行させます。


この方法は知識があれば細やかな制御が可能ですが、エラーメッセージ表示の抑制と異なり、エラー自体を無かったことにするため、お勧めできません。

エラー演算子「@」を未定義になりそうな変数名の頭につけてエラーを無視する。
$name = @$_POST[‘name’];
この方法はPHP8では機能しなくなりましたが、この記述がすでにあって、PHP7までこの方法で動作させていた場合はPHP8から動作しなくなることが考えられるため、ご参考ください。

エラー表示を抑制する方法は、いずれも緊急性があり、どうしても今PHPが動かないと困る場合などにご検討ください。

終りに

「Warning: Undefined variable」エラーを出す、PHP8のこの仕様変更は、PHPの可読性、メンテナンス性の悪さを改善する意図があるものと思われますが、この傾向はPHP9にも引き継がれ、より厳格になっていくと思われます。
プログラムの敷居が低いことによってシェアを獲得してきたPHPですが、ユーザーが多いからこそ、このような変更がPHP全体の品質を大きく押し上げるものとなると期待し、歓迎していきましょう。