🈳

定義されていないプロパティをemptyで判定して頭を抱えた話

2024/02/16に公開

はじめに

こんなクラスがあったとします。

class Foo {
    private array $bar;

    public function __construct()
    {
        $this->bar = [
            'test' => 'test',
        ];
    }

    public function __get(string $name): string
    {
        return isset($this->bar[$name]) ? $this->bar[$name] : '';
    }
}

変数 $bar はprivateなので外から直接触れませんが、Foo で定義されていないプロパティへのアクセスがあった場合、 $bar のキーから探して、あれば値を返却し、なければ空文字列を返却する、となっています。

では、以下を実行してみます。

$foo = new Foo();
echo $foo->test;
echo PHP_EOL;

if (empty($foo->test)) {
    echo 'Empty';
} else {
    echo 'Exists';
}

結果は。。

test
Empty

普通に考えて、 $foo->test$bar['test'] を参照するので、echo $foo->testtest を出力するのはわかります。
しかし、 empty($foo->test) で true を得るのがよくわかりません。あるのに無い?

なぜこうなるのか?

PHPのマニュアルには以下のような記述があります。
https://www.php.net/manual/ja/function.empty.php

変数が存在しなくても警告は発生しません。 つまり、empty() は本質的に !isset($var) || $var == false と同じことを簡潔に記述しているだけです。

定義されていないプロパティ test に対して isset を使用する場合は、__isset にてオーバーロードする必要が出てきます。Fooクラスには、__isset が宣言されていないため、isset では定義されていない test が存在しないと判断されるようです。
つまり、!isset($foo->test) === true となるため、empty($foo->test) === true となるとのことでした。

以下をFooクラスに追加すれば期待通りの出力を得られそうです。

public function __isset(string $name): bool
{
    return isset($this->bar[$name]);
}
test
Exists

おわりに

empty、難しいのであんまり使いたくない。。

Discussion