📚
PHPのempty()が__getだけでは正しく動作しない理由
PHPで__get
マジックメソッドを実装したクラスに対してempty()
を使うと、予想していない結果になることがあります。
コード例
<?php
class Accessor
{
public function __construct(private $target) {}
public function __get($key)
{
return $this->target->$key;
}
}
$school = new stdClass();
$school->name = "Tokyo";
$accessor = new Accessor($school);
var_dump($accessor->name); // "Tokyo"
var_dump(empty($accessor->name)); // true 注意
上記の場合Tokyo
という文字列が出力されるが、empty()
はtrue
になります。
なぜこうなるのか
なぜかというと、下記です。
__isset() は、 isset() あるいは empty() をアクセス不能(protected または private)または存在しないプロパティに対して実行したときに起動します。
https://www.php.net/__get#language.oop5.overloading.members
つまり、empty()
はアクセス不能なプロパティに対して内部的に__isset()
を呼び出します。__isset()
が定義されていない場合、プロパティは「存在しない」と判断され、empty()
は常にtrue
(空)を返します
解決策: __isset()も実装する
上記のコードを修正するならこのようにできます。
<?php
class Accessor
{
public function __construct(private $target) {}
public function __get($key)
{
return $this->target->$key;
}
public function __isset($key)
{
return isset($this->target->$key);
}
}
$school = new stdClass();
$school->name = "Tokyo";
$accessor = new Accessor($school);
var_dump($accessor->name); // "Tokyo"
var_dump(empty($accessor->name)); // falseに変わる
考えてみて
いろいろ考えてみて、__get()
を使いたいユースケースにもよるが、
個人的にはPHP 8.1以降であれば、public readonly
でいいのかなと思ったがどうなのだろう。
参考:
Discussion