PHPの曖昧判定を使う時に注意しておくべきこと

PHPの曖昧判定

PHPは厳密ではない比較をする関数がいくつかあります。
厳密比較をしないことによって意図しない判定をしてしまうことがあります。
今回はemptyを使った時の注意点を、実装とユニットテストの観点から見てみます。

ちなみに自分は絶対厳密比較しか許さないという派閥では全くなく、あくまで注意すべきくらいの気持ちでいます。

サンプルコードと注意点

例えば、空文字だった場合に処理したい内容を、emptyで比較するとします。
なお空文字以外で動作すると意図しない挙動になるコードと仮定します(サンプルは文字列返しているだけですが)。

実装

    public function checkEmpty(string $value)
    {
        if (empty($value)) {
            return "Value is empty";
        } else {
            return "Value is not empty";
        }
    }

テストコード

    public function test_空文字の時にValue is emptyを返すこと()
    {
        $sample = new Sample();
        $result = $sample->checkEmpty("");
        $this->assertSame($result, "Value is empty");
    }

    public function test_空文字の時にValue is not emptyを返すこと()
    {
        $sample = new Sample();
        $result = $sample->checkEmpty("a");
        $this->assertSame($result, "Value is not empty");
    }

一見このテストコードは返すパターンと返さないパターンをテストできているように見えます。
しかし、emptyは"0"もtrueを返します(PHP8.3現在)。

そのため、空文字の時のみ期待通りの動きをする、というテストが出来ていません。
なんなら実装自体も空文字の時だけ"Value is empty"を返す、という動きになっていません。

このような意図しない値の判定が生じうるケースは、if($var)などの判定でも起こり得ると思います。

エンジニアは前後のコードやこの関数がどのように呼ばれるか知っているため、"0"という値が現実的に入っていることはない、ということが分かってこのような実装をすることがあるかもしれません。実際問題ないケースがほとんどだと思います。
ただ、今後もこの関数がセーフティに使われることを保証するなら、曖昧な判定は避けたほうが好ましいと思います。

また、emptyif($var)を使う場合、判定の挙動を実装・ユニットテストで都度意識する必要があるため、実は厳密比較の方が考えることが少なくなるメリットもあると思います。

対応策

ではどうするかと言うと、なるべく厳密比較を用いるのが良いと思います。

修正したコード

    public function checkEmpty(string $value)
    {
        if ($value === "") {
            return "Value is empty";
        } else {
            return "Value is not empty";
        }
    }

上記コードは文字列を厳密比較しているので、"0"はelseに入ります。
またテストコードは先ほどのサンプルでしっかり返すパターンと返さないパターンをテスト出来ているようになりました。

曖昧にfalseyな値を判定することは便利なケースもありますし、使うべきではないとまでは思いませんが、意図しない判定にならないように注意することは大事ですね。