📘
動的型付け言語に不慣れな私がPHPのin_arrayの挙動に驚いた話
普段は滅多に触らないPHPのコードをレビューする機会があり、
in_array関数が私がこれまで実務で触ってきたC#やTypeScriptのような静的型付け言語にはない挙動をしたので簡単にまとめておきます。
(暗黙の型変換で予想だにしない動きをしました、、)
レビュー対象のコード
ざっくり書くと、以下のような感じです。
(説明をわかりやすくするために一部改変しています。)
<?php
// レビュー対象のコードの一部を切り取ってメソッド化
function processInputIgnoreType($param)
{
// レビュー対象のコードのrangeの第一引数は1だったが、0の時に予想外の挙動をしたのであえて0で記載
if (in_array($param, range(0, 10))) {
echo "{$param}は処理される範囲内のパラメータです\n";
}
}
// メソッドの引数には数値が文字列でわたってくる
// (説明をわかりやすくするために追記)
processInputIgnoreType('5'); // 文字列が数値に暗黙変換されることがわかった
processInputIgnoreType('20');
processInputIgnoreType('1' . '0'); // 文字列を連結させても数値に暗黙変換
processInputIgnoreType(' ' . '1' . ' '); // 文字列の前後のスペースは取り除いて比較する模様
processInputIgnoreType('ABCDあいうえお'); // PHP7.4まではこれもtrueになる
?>?>
上記のコードの実行結果は以下のようになります。
5は処理される範囲内のパラメータです
10は処理される範囲内のパラメータです
1 は処理される範囲内のパラメータです
ABCDあいうえおは処理される範囲内のパラメータです
直感的に意味の掴みにくい挙動かと思います。
静的型付け言語ばかり使ってきてよかったなぁと思いました。
※最後の不思議な挙動についてはこちらの記事にも言及が以下のようにあります。
文字列と数値を比較する場合には暗黙の間に文字列の型変換が行われて0になってしまう
PHP8.0で、数値を文字列にキャストして比較するようになったことでこの事象は解消されたようです。
防止策
in_arrayの第三引数にtrue
を指定すれば、型変換を行わずに比較することになり、
直感通りの比較結果になります。
※デフォルトはfalseなので、第三引数に何も指定しないと、型変換が行われた比較が実行されます。
コードを書き換えると以下のようになります。
<?php
function processInput($param)
{
// in_arrayの第三引数にtrueを指定した上で、配列の各要素が文字列になるようにする
if (in_array($param, array_map('strval', range(0, 10)), true)) {
echo "{$param}は処理される範囲内のパラメータです\n";
}
}
// メソッドの引数には数値が文字列でわたってくる
processInput('5');
processInput('20');
processInput('1' . '0');
processInput(' ' . '1' . ' ');
processInput('ABCDあいうえお');
?>
出力結果は見た目通り、以下のようになります。
5は処理される範囲内のパラメータです
10は処理される範囲内のパラメータです
まとめ
不慣れな言語を触る際は、慎重に動作確認すべきということを改めて感じました。
null、0、空配列、空オブジェクトなどの挙動など、予想と違うことが起きるかもしれないので、、、
Discussion