配列に存在しないキーを参照したときの警告です。isset()やnull合体演算子で防げます。
<?php
$user = ["name" => "Taro"];
echo $user["age"];
<?php
$user = ["name" => "Taro"];
echo $user["age"] ?? "未設定"; // null合体演算子
if (isset($user["age"])) {
echo $user["age"];
}
▶ 修正済みコードを実行エディタで開く
存在しないかもしれないキーはnull合体演算子??で既定値を指定します。$user["age"] ?? "未設定"ならキーが無くても警告になりません。isset()やarray_key_exists()で事前確認するのも有効です。
キー名が"age"のような文字列ではなく0や1の数字で出るときは、分割・抽出の結果が期待した件数に足りていないのがほぼ原因です。
explode()の結果 — 区切り文字が無い行では要素が1個しか返らず、[1]が存在しないpreg_match()の$m — マッチしなかった、またはそのカッコが無い($m[1]が無い)[2]などが欠ける$argv — コマンドライン引数を渡さずに実行している<?php
$line = "progo"; // カンマが無い行
$cols = explode(",", $line);
echo $cols[1]; // Warning: Undefined array key 1
OK
<?php
$line = "progo";
$cols = explode(",", $line);
echo $cols[1] ?? "(2列目なし)";
// 件数を確かめてから使うとより安全
if (count($cols) >= 2) {
echo $cols[1];
}
▶ このコードを実行エディタで開く
同じ状況でも、PHPのバージョンによってメッセージと深刻度が変わります。古い記事の「Undefined index」と今の「Undefined array key」は同じ現象です。
| バージョン | メッセージ | レベル |
|---|---|---|
| PHP 7.x以前 | Notice: Undefined index: age | Notice(表示されないことも多い) |
| PHP 8.0以降 | Warning: Undefined array key "age" | Warning(既定で表示される) |
「PHP 8に上げたら急にWarningが大量に出た」のは、もともとあったバグが見えるようになっただけです。警告を抑制せず、キーの有無を確認するコードに直すのが正しい対応です。なお変数そのものが無い場合はUndefined variableになります。
実務でこのWarningが出る場所の大半はフォーム入力です。送られてこない可能性を前提に書きます。チェックボックスは未チェックだとキー自体が送られない点に注意してください。
OK<?php
// 既定値つきで受け取る
$name = $_POST["name"] ?? "";
$agree = isset($_POST["agree"]); // チェックボックスは有無で判定
$page = (int)($_GET["page"] ?? 1); // 数値は型変換もセットで
// 空文字も既定値にしたいときは ?: を併用
$sort = ($_GET["sort"] ?? "") ?: "new";
// 必須項目はまとめて検証する
$required = ["name", "email"];
$missing = array_diff($required, array_keys($_POST));
if ($missing) {
echo "未入力: " . implode(", ", $missing);
}
| 書き方 | キーが無い | 値がnull | 値が"" / 0 |
|---|---|---|---|
$a["k"] ?? "既定" | 既定値(警告なし) | 既定値 | そのまま使う |
isset($a["k"]) | false | false | true |
array_key_exists("k", $a) | false | true | true |
empty($a["k"]) | true(警告なし) | true | true(注意) |
empty()は"0"や0も「空」と判定するため、数量や件数の判定に使うと不具合になります。キーの有無を見たいだけなら??かisset()、値がnullでもキーの存在を知りたいならarray_key_exists()が正解です。
APIのJSONや設定配列のように階層が深い場合、??は途中の階層が無くても警告を出さずに済みます。
<?php
$json = '{"user":{"profile":{"name":"Taro"}}}';
$data = json_decode($json, true);
// 途中の階層が無くても警告なし
$name = $data["user"]["profile"]["name"] ?? "名無し";
$city = $data["user"]["address"]["city"] ?? "未登録";
echo $name, " / ", $city;
デバッグ時はvar_dump($data);やprint_r(array_keys($data));で実際に入っているキーを確認するのが近道です。キー名の全角・半角、前後の空白、大文字小文字の違いもよくある原因です。
error_reporting(E_ALL & ~E_WARNING);や@$user["age"]で警告は消せますが、原因は残ったままで、後により深刻な不具合として現れます。本番環境では画面に出さない設定(display_errors=Off+ログ出力)にしつつ、コード側は??で直すのが正しい運用です。