コードレビューをしていたところ、「こんなの初めてっ!」ってくらい大量の is_null() を見た。ところによっては empty() や isset() が使われているので、意識して使い分けてるのか?と思ったけど、どうやら気分だったようなので、以下のような点を説明して直してもらった。
PHP における NULL 判定
isset(), empty(), is_null() の比較
変数の値($var) | isset($var) | empty($var) | is_null($var) |
---|---|---|---|
""(空文字) | ture | ture | |
" "(スペース) | ture | ture | |
FALSE | ture | ture | |
TRUE | ture | ||
array() (空配列) | ture | ture | ture |
NULL | true | ||
"0" (文字列の 0) | true | true | |
0 (整数の 0) | true | true | |
0.0 (実数の 0) | true | true | |
var $var (宣言されたがセットされていない変数) | true | true | |
NULL byte ("\0") | true |
単純に NULL かどうかを判定したい場合、
$var !== null > !isset($var) > !empty($var) > !$var > !is_null($var)
の順で遅くなっていく。is_null() は関数なので呼び出しのオーバーヘッドがあって遅い。
isset() がよく使われてるような気がするが、NULL 判定だけなら、!== を使うのが良い。
== と === の比較
==/=== | TRUE | FALSE | 1 | 0 | -1 | "1" | "0" | "-1" | NULL | array() | "php" | "" |
---|---|---|---|---|---|---|---|---|---|---|---|---|
TRUE | true/true | true/false | true/false | true/false | true/false | true/false | ||||||
FALSE | true/true | true/false | true/false | true/false | true/false | true/false | ||||||
1 | true/false | true/true | true/false | |||||||||
0 | true/false | true/true | true/false | true/false | true/false | true/false | ||||||
-1 | true/false | true/true | true/false | |||||||||
"1" | true/false | true/false | true/true | |||||||||
"0" | true/false | true/false | true/true | |||||||||
"-1" | true/false | true/false | true/true | |||||||||
NULL | true/false | true/false | true/true | true/false | true/false | |||||||
array() | true/false | true/false | true/true | |||||||||
"php" | true/false | true/false | true/true | |||||||||
"" | true/false | true/false | true/false | true/true | ||||||||
あってるかな? |
詳細は見にくいけど PHP 型の比較表
適当なプログラムで確認。
<?php
$evaluateArray = array (NULL, 0, "0", "php");
$evaluateArrayDisplay = array ('NULL', '0',"\"0\"", "\"php\"");
foreach ($evaluateArray as $k => $var1) {
foreach ($evaluateArray as $l => $var2) {
if ($l > $k) {
if ($var1 == $var2) echo "$evaluateArrayDisplay[$k] == $evaluateArrayDisplay[$l]\n";
if ($var1 != $var2) echo "$evaluateArrayDisplay[$k] != $evaluateArrayDisplay[$l]\n";
if ($var1 === $var2) echo "$evaluateArrayDisplay[$k] === $evaluateArrayDisplay[$l]\n";
if ($var1 !== $var2) echo "$evaluateArrayDisplay[$k] !== $evaluateArrayDisplay[$l]\n";
}
}
}
exit;
結果
$ php --version
PHP 7.1.13 (cli) (built: Jan 25 2018 22:40:51) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.1.13, Copyright (c) 1999-2017, by Zend Technologies
$ php sample.php
NULL == 0
NULL !== 0
NULL != "0"
NULL !== "0"
NULL != "php"
NULL !== "php"
0 == "0"
0 !== "0"
0 == "php"
0 !== "php"
"0" != "php"
"0" !== "php"
ん〜、0 キモい。
MySQL の場合
例えば、こんな Address テーブルがあったとして
id(int) | pref_cd(int) | city_cd(int) | name(varchar) | roman(varchar) |
---|---|---|---|---|
745 | 14 | 0 | 神奈川県 | NULL |
746 | 14 | 100 | 横浜市 | yokohama |
747 | 14 | 101 | 鶴見区 | yokohama_tsurumi |
色々 select してみる。
mysql> select version();
+------------+
| version() |
+------------+
| 5.6.35-log |
+------------+
1 row in set (0.02 sec)
mysql> select * from address where city_cd = 0;
+-----+---------+---------+--------------+-------+
| id | pref_cd | city_cd | name | roman |
+-----+---------+---------+--------------+-------+
| 745 | 14 | 0 | 神奈川県 | NULL |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)
mysql> select * from address where city_cd = '0';
+-----+---------+---------+--------------+-------+
| id | pref_cd | city_cd | name | roman |
+-----+---------+---------+--------------+-------+
| 745 | 14 | 0 | 神奈川県 | NULL |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)
mysql> select * from address where city_cd = '';
+-----+---------+---------+--------------+-------+
| id | pref_cd | city_cd | name | roman |
+-----+---------+---------+--------------+-------+
| 745 | 14 | 0 | 神奈川県 | NULL |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)
mysql> select * from address where city_cd = null;
Empty set (0.00 sec)
mysql> select * from address where city_cd is null;
Empty set (0.00 sec)
mysql> select * from address where isnull(city_cd);
Empty set (0.00 sec)
mysql> select * from address where roman = 0;
+-----+---------+---------+-----------+------------------+
| id | pref_cd | city_cd | name | roman |
+-----+---------+---------+-----------+------------------+
| 746 | 14 | 100 | 横浜市 | yokohama |
| 747 | 14 | 101 | 鶴見区 | yokohama_tsurumi |
+-----+---------+---------+-----------+------------------+
2 rows in set, 2 warnings (0.00 sec)
mysql> select * from address where roman = '0';
Empty set (0.00 sec)
mysql> select * from address where roman = '';
Empty set (0.00 sec)
mysql> select * from address where roman = null;
Empty set (0.00 sec)
mysql> select * from address where roman is null;
+-----+---------+---------+--------------+-------+
| id | pref_cd | city_cd | name | roman |
+-----+---------+---------+--------------+-------+
| 745 | 14 | 0 | 神奈川県 | NULL |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)
mysql> select * from address where isnull(roman);
+-----+---------+---------+--------------+-------+
| id | pref_cd | city_cd | name | roman |
+-----+---------+---------+--------------+-------+
| 745 | 14 | 0 | 神奈川県 | NULL |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)
PHP と MySQL で NULL や 0 の扱いが少し違う。
PHP に素で書いてある SQL とかあんまりレビューしたくないけど、クエリを作る際に、"= 0" とか "= null" とかが混じってバグってるとか普通にありそうで、見直したくない。