はじめに
入力値が空かどうかの判定に empty() を使うと、0 の1文字が空として扱われます。
0 を入力したのに、未入力と同じ扱いになります。
この記事では、空判定に使う書き方が 0 をどう扱うのかと、空判定をどう書けばよいのかを扱います。
環境: PHP 8.5.9
入力値の空判定に empty() を使うと 0 が空になる
入力された値があればそれを使い、無ければ既定値を使うコードです。
/**
* 使う値を決める
* 入力された値 → 既定値、の順で使う
* @param array $config
* @return string
*/
function resolveValue(array $config): string
{
if (!empty($config['userValue'])) {
return $config['userValue'];
}
return $config['defaultValue'];
}
abc を渡せばそのまま返りますが、0 を渡すと既定値が返ります。
var_dump(resolveValue(['userValue' => 'abc', 'defaultValue' => '既定値'])); // string(3) "abc"
var_dump(resolveValue(['userValue' => '0', 'defaultValue' => '既定値'])); // string(9) "既定値"
empty() と三項演算子の短縮形は、同じ判定をしている
empty() だけの問題ではありません。
空判定によく使う三項演算子の短縮形 ?: も、"0" を空と判定します。
empty() は "0" に対して true を返す
PHPマニュアル > empty には、戻り値について次のように書かれています。
varが存在しない、 または空や0の値が設定されている場合、 つまり boolean のコンテキストでfalseと見なされる場合、trueを返します。
文字列の "0" は、booleanコンテキストで false と評価されます。
そのため empty() は true を返します。
var_dump(empty('0')); // bool(true)
var_dump(empty('abc')); // bool(false)
三項演算子の短縮形 ?: は "0" のとき右辺を返す
PHPマニュアル > 比較演算子 には、三項演算子の短縮形について次のように書かれています。
三項演算子のまんなかの部分をなくすこともできます。 式
expr1 ?: expr3の結果は、expr1 がtrueと同等の場合は expr1、 それ以外の場合は expr3 となります。 この場合、expr1 は一度だけ評価されます。
「true と同等」かどうかの判定は、empty() と同じfalsy判定です。
そのため左辺が "0" のときは、右辺が返ります。
var_dump('0' ?: '既定値'); // string(9) "既定値"
var_dump('abc' ?: '既定値'); // string(3) "abc"
どちらも、書き方は違っても判定しているものは同じです。
値があるかどうかを確かめたつもりで、booleanに変換したときに真かどうかを確かめていることになります。
空判定は空文字との比較で書く
判定したいのは未入力かどうかです。
falsy判定をやめて、空文字との比較に変えます。
キーが存在しない場合に備えて、?? で空文字を補ってから比較します。
修正後のコードです。
function resolveValue(array $config): string
{
// 「0」の1文字も入力できるため、empty()や三項演算子の短縮形では未入力と区別できない
if (($config['userValue'] ?? '') !== '') {
return $config['userValue'];
}
return $config['defaultValue'];
}
var_dump(resolveValue(['userValue' => '0', 'defaultValue' => '既定値'])); // string(1) "0"
まとめ
- 入力値の空判定に
empty()を使うと、0の1文字が未入力と同じ扱いになります - 三項演算子の短縮形
?:も同じfalsy判定なので、左辺が"0"のときは右辺の値になります - 未入力かどうかを判定したいのであれば、falsy判定ではなく
nullや空文字との比較で書きます