PHPで「Warning: Uninitialized string offset」は、文字列の存在しない位置(オフセット)にアクセスしたときに出る警告です。ループのインデックスのずれが原因のことが多く、さらに日本語などの多バイト文字では別の落とし穴もあります。この記事では原因と正しい対処をまとめます。
この記事の結論:存在しない位置にアクセスすると発生します。アクセス前に
isset($str[$offset]) で範囲を確認しましょう。また日本語などは $str[$i] が「バイト単位」になり文字化けするため、mb_substr() を使います。エラーの原因
文字列の長さを超えた位置にアクセスすると発生します。インデックスは0始まりなので、長さ5の文字列の有効な位置は 0〜4 です。$str[5] は存在しないため警告になります。
範囲外アクセス
<?php $str = "Hello"; // 長さ5、有効な位置は 0〜4 echo $str[5]; // Warning: Uninitialized string offset 5
このメッセージはPHP 8.0で Notice から Warning に格上げされました(PHP 7では Notice)。いずれも処理は止まりませんが、取得結果は空になります。
対処法:issetで範囲を確認する
アクセス前に isset($str[$offset]) で確認します。範囲外なら false を返すため、安全に分岐できます。
issetでガード
<?php
$str = "Hello";
$offset = 5;
if (isset($str[$offset])) {
echo $str[$offset];
} else {
echo "その位置に文字はありません";
}
よくある原因:ループのオフセットずれ
実際によくあるのが、ループ条件のoff-by-one(1つずれ)です。<= を使うと最後に範囲外へアクセスしてしまいます。strlen() 未満(<)で回すのが正解です。
NG / OK: ループ条件
<?php
$str = "Hello";
// NG: <= だと最後に $str[5] にアクセスして警告
for ($i = 0; $i <= strlen($str); $i++) {
echo $str[$i];
}
// OK: < で回す
for ($i = 0; $i < strlen($str); $i++) {
echo $str[$i];
}
補足:負のオフセットは末尾から数える
PHPでは負のオフセットが使え、末尾から数えた文字を取得できます。$str[-1] は最後の文字です(ただし範囲外の負の値はやはり警告になります)。
負のオフセット
<?php $str = "Hello"; echo $str[-1]; // o(最後の文字) echo $str[-5]; // H(先頭)
最大の落とし穴:日本語など多バイト文字
ここが重要です。$str[$i] は「バイト単位」でのアクセスです。UTF-8の日本語は1文字が3バイトなどになるため、$str[0] は壊れた1バイトを返し、文字化けやオフセットずれの原因になります。
NG: 多バイト文字をバイトで取ると壊れる
<?php $str = "あいう"; echo strlen($str); // 9(バイト数) echo mb_strlen($str); // 3(文字数) var_dump($str[0]); // string(1) "?" ← 壊れた1バイト
日本語などを1文字ずつ正しく扱うには、mb_substr() を使います。ループも mb_strlen() で回します。
OK: mb_substrで多バイト対応
<?php
$str = "あいう";
echo mb_substr($str, 0, 1); // あ(正しく1文字)
// 1文字ずつ処理する
for ($i = 0; $i < mb_strlen($str); $i++) {
echo mb_substr($str, $i, 1) . "\n"; // あ / い / う
}
// PHP 7.4+ なら mb_str_split も便利
foreach (mb_str_split($str) as $ch) {
echo $ch . "\n";
}
日本語を含む文字列で
$str[$i] を使うのは避けましょう。バイト単位アクセスになり、文字化け・オフセットずれ・このエラーの温床になります。mb_substr() / mb_str_split() を使うのが安全です。文字の取り出し(
substr と mb_substr)は文字列の文字取得方法: substrとmb_substrを使いこなそう、正確な文字数の取得は文字列の長さを正確に取得する方法で詳しく解説しています。よくある質問(FAQ)
Qこのエラーは致命的ですか?
Aいいえ、Warning(警告)です(PHP 7ではNotice、PHP 8で警告に格上げ)。処理は止まりませんが取得結果は空になるため、原因は直しておきましょう。
Qループの最後でだけエラーが出ます。
Aループ条件が
<= になっている可能性が高いです。インデックスは0始まりなので、$i < strlen($str) で回してください。Q日本語の文字列で$str[0]がおかしくなります。
A
$str[$i] はバイト単位のアクセスだからです。UTF-8の日本語は1文字が複数バイトなので壊れます。mb_substr($str, $i, 1) を使い、長さは mb_strlen() で取得してください。Q存在チェックはissetとempty、どちらが良いですか?
A範囲チェックには
isset($str[$offset]) が適しています。範囲外なら false を返します。なお $str[$i] が "0" の文字でも isset は true を返すので、empty より安全です。まとめ
このエラーへの対処を整理します。
- 原因は範囲外の文字位置へのアクセス(0始まり)
- アクセス前に
isset($str[$offset])で確認 - ループは
$i < strlen($str)(<=は off-by-one) - 負のオフセット
$str[-1]は末尾から数える - 日本語など多バイトは
mb_substr()($str[$i]はバイト単位で壊れる)
関連として、substrとmb_substrの使い方・文字列の長さを正確に取得する方法・Undefined variable の解決方法もあわせて読むと、PHPの文字列処理に強くなれます。
