[PHP]型に左右されないin_array()を考える

Read More



PHPのin_array()は何も考えずに使うと問題があるということを以前の記事の最後で少し触れた。

以前の記事
■[PHP]簡易比較でおちいりやすい罠_その3
http://xirasaya.com/?m=detail&hid=521


記事の最後に書いたこの問題。
■in_array()の問題
<?php
$var='';
if(in_array($var, array(0,1,2)))
{
  echo 1;
}
else
{
  echo 0;
}
?>



■結果
> 1

こう返ってくるわけだ。


これを回避するにはin_array()の第3引数のstrictでtrueを指定すればよい。
こうすることで厳密な型判定も加えてくれるようになる。

だが、第3引数のstrictを使うと今度は新たな問題が発生する。
厳密に型判定し比較を行うため$var='1'と$var=1で結果が違ってくる。


念のためその例を残す。
■in_array()のstrictによる弊害
<?php
$test='1';
if(in_array($test, array('1'), true))
{
  echo 'true';
}
else
{
  echo 'false';
}

$test=1;
if(in_array($test, array('1'), true))
{
  echo 'true';
}
else
{
  echo 'false';
}
?>



■結果
$test='1' // true
$test=1  // false



先述した二つの例が示すように、一方では第3引数はあったほうがよくて、もう一方では第3引数はなしの方が都合がよい。

つまり第3引数を使いこなすには、
あらかじめ第1引数と第2引数のそれぞれに格納されている型を把握しておく必要が出てくる。

こうなるとPHPのメリットが失われてくる。


string型とint型とを意識せず、それでいてどちらの例も理想の結果になるような関数を考えてみようと思ふ。

■in_array()を改良する
<?php
/**
 * 配列に値があるかチェックする
 * ****************************************40
 * in_array()の拡張
 * string型とint型の違いを吸収する。
 *  ''(空文字), null, falseは厳密に区別する。
 * 文字列型で末尾0の小数値は必ず文字列型として扱う。
 * 
 * in_array()にはin_array('test', array(0))でtrueが返る問題がある。
 * 第3引数のstrictを指定すれば厳格な比較(型)をするがそれはそれで困る場合がある。数値の1と文字列の'1'など。
 * こういった問題を意識せずに使えるようにしたのが本関数。
 * 
 * @param mixed $needle
 * @param array $haystack
 * @param string $strict
 * @return boolean // 配列で needle が見つかった場合に TRUE、それ以外の場合は、FALSE を返します。 
 */
function my_in_array($needle=null, $haystack=array(), $strict=false)
{
  $ret=true;

  if(!strlen($needle) or $strict)	// null, '', false...
  {
    $ret = in_array($needle, $haystack, true);
  }
  else
  {
    $ret = !!preg_grep('/^'.preg_quote($needle, '/').'$/u', $haystack);
  }

  return $ret;
}
?>



これを使って色々なパターンを試してみる。


echoecho(in_array('test', array(null, 0, false)));  // 1
echoecho(in_array('test', array(null, 0, false), true));  // 0
echoecho(my_in_array('test', array(null, 0, false)));  // 0

echoecho(in_array(1, array('1')));  // 1
echoecho(in_array(1, array('1'), true));  // 0
echoecho(my_in_array(1, array('1')));  // 1

echoecho(in_array('1', array('1')));  // 1
echoecho(in_array('1', array('1'), true));  // 1
echoecho(my_in_array('1', array('1')));  // 1

echoecho(in_array(0, array(false, '', null)));  // 1
echoecho(in_array(0, array(false, '', null), true));  // 0
echoecho(my_in_array(0, array(false, '', null)));  // 0

echoecho(in_array(false, array('', null, 0)));  // 1
echoecho(in_array(false, array('', null, 0), true));  // 0
echoecho(my_in_array(false, array('', null, 0)));  // 0

echoecho(in_array('', array(null, 0, false)));  // 1
echoecho(in_array('', array(null, 0, false), true));  // 0
echoecho(my_in_array('', array(null, 0, false)));  // 0
?>
※echoecho()は自作関数
※戻り値はbooleanだがここでは視覚的にわかりやすくするため0と1で表記



うん、my_in_array()は型を意識せずに理想的な結果が返ってきている





-- 2016/10/21 一部文言修正。
structじゃなくてstrictだた