PHPのarray_searchが全く見当違い(に見える)値を出力する件について(ちゃんとstrictフラグ立てよう)

by @dekokun on 2014/04/26 20:40

Tagged as: PHP.

どうも。来月結婚する@dekokunです。タイトルで言いたいことは全て言っているのですが、まぁ詳細書きます。

導入 array_searchとは

指定した値を配列で検索し、見つかった場合に対応するキーを返す

参照:array_search

<?php
array_search(1, [2, 3, 4, 1]); // => 3
// インデックス3の値が1なので、3が返ってきた
array_search(1, [2, 1, 4, 1]); // => 1
// 複数マッチした場合は最初にマッチした値を返す

という感じですね。

本題

では、タイトルにあるように“array_searchが全く見当違い(に見える)値を出力する”というのはどういうことか見て行きましょう。

<?php

function test() {
  // 'hoge'なんて引数の配列の中に存在しないのでfalseが返るよね!
  if (array_search('hoge', [1, 2, 3, 0])) {
    echo 'match!';
    return;
  }
  echo 'not match!';
}

test();
// => "match!"が出力される!!!

何が起きているかわかりますかね。私は最初わかりませんでしたよ。ま、そもそも上記のように配列の中に値があるかどうか調べたい場合はin_arrayを使うべきではありますが、まぁ。

REPLを使ってもう少し詳しく確かめてみましょう(余談ですが、PHPのREPLとしては私はpsyshを使ってます。便利ですね。他の人のPC上で実施するときはphp -a使いますが、結構だるいですよね。)

$ psysh
>>> array_search('hoge', [1, 2, 3, 0])
=> 3

上記、なぜか3が出力されてますね。配列の中のインデックスが3の値は…0 !!!

はい、PHPによく慣れている方はもうわかったかもしれませんね。array_searchはデフォルトでは“==”による比較を行うため、‘hoge’ == 0の比較を行ってtrueになってしまったということだったのでした。ちょっとぎょっとしますよねこれは。

解決策

array_searchにはstrictフラグという、値の比較の際に型の一致(及びオブジェクトの一致)をチェックするフラグがあるのでそちらを使いましょう。

$ psysh
// strictモードfalse(デフォルト値)版
>>> array_search('hoge', [1, 2, 3, 0])
=> 3
// strictモードfalseを明示的に指定版
>>> array_search('hoge', [1, 2, 3, 0], false)
=> 3
// strictモードtrue版
>>> array_search('hoge', [1, 2, 3, 0], true)
=> false
// やった!これからはちゃんとstrictにしよう!!!

めでたしめでたしなのでしたー!!!

本題とは関係ないけどarray_searchについて

array_searchについて学んだついでに、上記の他に「array_searchのバグじゃないか」としてよく言われる挙動を見ておきましょう。

<?php

function test() {
  // 1は引数の配列の中に存在するし"match"が出力されるよね
  if (array_search(1, [1, 2, 3])) {
    echo 'match!';
    return;
  }
  echo 'not match!';
}

test();
// => "not match!"が出力される!!!

上記、PHPによく慣れている方は、すぐに「あ、検索する値が配列の0番目にあったのでarray_searchの返り値が0になりfalse判定されたんですね」とわかるかと思います。 まぁ、こちらはわかりやすいですかね。PHPを書いていればあまりにも普通のことです。

ま、上記のようにマッチしているかどうかを取りたいだけなら問答無用でin_arrayを使えという話ではありますが(なお、in_arrayもstrictフラグを使わないとarray_searchと同様文字列が0にマッチしたりするので要注意です。)

あとがき

PHPの曖昧さとうまく付き合っていく方法が未だによくわかっていない。というか、‘hoge’ == 0というのはあまりにも直感的じゃなさすぎてさすがに無理がある。‘1a’ == 1とかも まぁ、とにかく厳密にできるところは一歩ずつ厳密にしていきましょう。

あ、「来月結婚する」って書いたけど正確には入籍は一年くらい前に終わっていて、来月あるのは挙式です。

1年半前から毎月1記事は書こうと頑張っていたが結婚準備でかなりバタバタしており、「4月は頑張らないと」と思っていたのですが、まぁブログは予定通り書けてよかったです。

このブログを140文字で伝えると

以下私のツイート

comments powered by Disqus