はじめに
先日、Twitterでこんな投稿をしました。
is_nullもissetもemptyも絶妙に自分のニーズに合わないので
— 佐藤裕/株式会社カジトリ代表 (@hirossyi73) November 7, 2019
is_nullorempty自作して使ってます
null→true
""→true
0→false
array(0)→true
"foo"→false
1→false https://t.co/tRZPWWeoI1
今回はこの話を、もう少し掘り下げて話を進めていきたいと思います。
isset, empty, is_nullどれも絶妙に微妙
PHPのnull・空判定には、主に「isset, empty, is_null」の3つが用意されています。
しかし、これらの関数は、私にとって絶妙に微妙でした。
まずは、これらの関数の結果を改めて見てみます。なおPHP isset, empty, is_null の違い早見表からの引用です。
値 | if($var) | isset | empty | is_null |
---|---|---|---|---|
$var=1 | true | true | false | false |
$var=""; | false | true | true | false |
$var="0"; | false | true | true | false |
$var=0; | false | true | true | false |
$var=NULL; | false | false | true | true |
$var | false | false | true | true |
$var=array() | false | true | true | false |
$var=array(1) | true | true | false | false |
空判定したい時は、「入力チェックを行いたい時」
さて、皆さんがこれらの空判定関数を使用したい時は、主にどんな状況でしょうか?
色々あると思いますが、私が思うもっとも頻出な場面として、
ユーザーやシステムが、値を入力したかどうかの必須チェックを行う
時だと思います。
ユーザーの画面入力、CSVからの取込やデータベースからのデータ取得など、多くの場面で出てくる、「値の有無チェック」。
そのような前提を考えた時、上記の3関数だと、実はどれも絶妙にマッチしないんです。
なので、このような入力チェックで完全に判定できるような関数、「is_nullorempty」を作成しよう!というのが、今回の目的です。
※issetとemptyは厳密には関数じゃないらしいですが、ここでは関数としちゃいます
空判定したい内容の分析
ここからは、各値ごとに、「空判定すべきかどうか」のあるべき姿の分析を行っていきます。この値は空としたいかどうか?という判断ですね。
実際に入力チェックを行った時に、この値は空にしたいかどうか?という観点で見ていきます。
※なお、ここでは「空」は、「値がない」ということを指します。
またこれ以降、空として扱いたい場合は"空とする"と、空として扱いたくない場合は"空としない"と記載していきます。
■ null
これは間違いなく"空とする"ですね。
■ 空文字""
CSVやExcel取込などでよく発生する、「空文字""」の場合。
関数issetだとtrueになる("空としない"となる)この値ですが、これは間違いなく**"空とすべき"**でしょう。
ユーザーが何も入力していないから、空文字となって返ってくるわけです。そのため、空文字は"空とする"として考えます。
■ 0
関数emptyだとtrueになる("空とする"となる)この値ですが、これはどうでしょうか?
ユーザーが「0」と入力した時、CSVに「0」と入力されていた時。これは「未入力」として扱うべきでしょうか。違うと思います。
そのため、0は"空としない"として考えます。もちろん文字列"0"も同様です。
■ array()
要素がない配列。これはユーザーやシステムが値をちゃんと入れていないから、要素数が0の配列が出来ているわけです。
そのため、要素数0の配列は"空とする"として考えます。
■ " "(半角スペース)、ならびに" "(全角スペース)のみの文字列
さて半角・全角スペースのみの文字列。これはどうでしょうか。これに関しては判断が迷いそうです。
これは私の考え方ですが、状況によりけりだと考えます。常に"空とする"としても、"空としない"としても、場合によって色々と異なってきそうです。
そこでどうするかというと、スペースのみを"空としない"とする"is_nullorempty"と、"空とする"とする"is_nullorwhitespace"の両方を作ってしまっていいと考えます。
両方作った上で、「基本はこちらを使用する」というルールを決めてしまう。もしくは、あえてどちらかしか実装しない。という方針が良いと思います。
ちなみにこの関数名ですが、C#から来ています。String.IsNullOrEmptyとString.IsNullOrWhiteSpaceがあります。
空判定まとめ
これが私の考える、空判定のあるべき姿です。
|値|is_nullorempty|is_nullorwhitespace|
|:--|:--:|:--:|:--:|:--:|
|$var=1|false|false|
|$var="";|true|true|
|$var="0";|false|false|
|$var=0;|false|false|
|$var=NULL;|true|true|
|$var|-|-|
|$var=array()|true|true|
|$var=array(1)|false|false|
|$var=" "|false|true|
|$var=" "|false|true|
※"$var"について、undefinedな変数を関数に代入した時点でエラーになっちゃうので、判定できません。そこだけは欠点ですね
実装
では実装に入りたいと思います。
なお実装方法については、もっといい方法がありそうです(・∀・)
<?php
if (!function_exists('is_nullorempty')) {
/**
* validate string. null is true, "" is true, 0 and "0" is false, " " is false.
*/
function is_nullorempty($obj)
{
if($obj === 0 || $obj === "0"){
return false;
}
return empty($obj);
}
}
if (!function_exists('is_nullorwhitespace')) {
/**
* validate string. null is true, "" is true, 0 and "0" is false, " " is true.
*/
function is_nullorwhitespace($obj)
{
if(is_nullorempty($obj) === true){
return true;
}
if(is_string($obj) && mb_ereg_match("^(\s| )+$", $obj)){
return true;
}
return false;
}
}
/**
* テスト用
*
* @param [type] $value
* @return void
*/
function strbool($value)
{
return $value ? 'true' : 'false';
}
// テスト。ザル
echo('is_nullorempty "foo" is ' . strbool(is_nullorempty("foo")));
echo('<br />is_nullorempty 1 is ' . strbool(is_nullorempty(1)));
echo('<br />is_nullorempty "" is ' . strbool(is_nullorempty("")));
echo('<br />is_nullorempty "0" is ' . strbool(is_nullorempty("0")));
echo('<br />is_nullorempty 0 is ' . strbool(is_nullorempty(0)));
echo('<br />is_nullorempty NULL is ' . strbool(is_nullorempty(null)));
echo('<br />is_nullorempty array() is ' . strbool(is_nullorempty(array())));
echo('<br />is_nullorempty array(1) is ' . strbool(is_nullorempty(array(1))));
echo('<br />is_nullorempty " " is ' . strbool(is_nullorempty(" ")));
echo('<br />is_nullorempty " " is ' . strbool(is_nullorempty(" ")));
echo('<br />');
echo('<br />is_nullorwhitespace "foo" is ' . strbool(is_nullorwhitespace("foo")));
echo('<br />is_nullorwhitespace 1 is ' . strbool(is_nullorwhitespace(1)));
echo('<br />is_nullorwhitespace "" is ' . strbool(is_nullorwhitespace("")));
echo('<br />is_nullorwhitespace "0" is ' . strbool(is_nullorwhitespace("0")));
echo('<br />is_nullorwhitespace 0 is ' . strbool(is_nullorwhitespace(0)));
echo('<br />is_nullorwhitespace NULL is ' . strbool(is_nullorwhitespace(null)));
echo('<br />is_nullorwhitespace array() is ' . strbool(is_nullorwhitespace(array())));
echo('<br />is_nullorwhitespace array(1) is ' . strbool(is_nullorwhitespace(array(1))));
echo('<br />is_nullorwhitespace " " is ' . strbool(is_nullorwhitespace(" ")));
echo('<br />is_nullorwhitespace " " is ' . strbool(is_nullorwhitespace(" ")));
// テスト結果
// is_nullorempty "foo" is false
// is_nullorempty 1 is false
// is_nullorempty "" is true
// is_nullorempty "0" is false
// is_nullorempty 0 is false
// is_nullorempty NULL is true
// is_nullorempty array() is true
// is_nullorempty array(1) is false
// is_nullorempty " " is false
// is_nullorempty " " is false
//
// is_nullorwhitespace "foo" is false
// is_nullorwhitespace 1 is false
// is_nullorwhitespace "" is true
// is_nullorwhitespace "0" is false
// is_nullorwhitespace 0 is false
// is_nullorwhitespace NULL is true
// is_nullorwhitespace array() is true
// is_nullorwhitespace array(1) is false
// is_nullorwhitespace " " is true
// is_nullorwhitespace " " is true
?>
まとめ
- 値の入力チェックは、基本3関数どれも微妙なので、自作の関数を作ろう
- "is_nullorempty"と"is_nullorwhitespace"の両方作って、スペースのみを振り分けよう
- "タブのみ"って空とすべきなのか・・・?