🔤

PHP 8.0 以降に文字列のオフセットにアクセスした際の挙動の違い

2025/02/26に公開

どういうやつ?

こういうやつ

$str = 'The world';
var_dump($str[0]); // string(1) "T"

結論

文字列のオフセットに数値形式の文字列を指定した場合、intfloat で挙動が変化する。
'1', ' 1 ' および '0001' など int として扱われる定した場合は普通に int として扱われるが、
'1.0' および '+1.0E0' など float として扱われる文字列を指定した場合は TypeError になる。

実際にやってみた

全て PHP 8.0 以降です。

$str = 'The world';
var_dump($str['1']);    // string(1) "h"
var_dump($str[' 1 ']);  // string(1) "h"
var_dump($str['0001']); // string(1) "h"

var_dump($str['1.0']);    // Fatal error: Uncaught TypeError: Cannot access offset of type string on string
var_dump($str['+1.0E0']); // Fatal error: Uncaught TypeError: Cannot access offset of type string on string

上記のオフセットの値を文字列ではなく数値にした場合はこうなる。

$str = 'The world';
var_dump($str[1]);    // string(1) "h"
var_dump($str[ 1 ]);  // string(1) "h"
var_dump($str[0001]); // string(1) "h"

var_dump($str[1.0]);    // string(1) "h" Warning: String offset cast occurred
var_dump($str[+1.0E0]); // string(1) "h" Warning: String offset cast occurred

単純に float を指定する分には Warning は出るけど TypeError にはならない。
同じ数値形式の文字列でも挙動が変わるのは意外だった。
php-src を読んで原因を探ってみたい。

おまけ

数値から始まる文字列を指定すると別の Warning: Illegal string offset が出る。
また文字列の長さ以上のマイナス値を指定し、そこに代入しようとしても同じ Warning が起きる。

$str = 'The world';
var_dump($str['1str']); // string(1) "h" Warning: Illegal string offset "1str"
var_dump($str[-10] = 'foo'); // NULL Warning: Illegal string offset -10
var_dump($str); // The world

この Warning: Illegal string offset の起こし方を研究していて今回の現象を観測したので記事にしてみました。
他にも Illegal string offset の起こし方のパターンを知っている方がいたら教えていただけると嬉しいです。

見た資料

https://www.php.net/manual/ja/language.types.string.php#language.types.string.substr
https://wiki.php.net/rfc/saner-numeric-strings

Discussion