PHP: связь между __isset() и __get()

Недавно у меня было короткое обсуждение с другим разработчиком об отношениях между PHP __isset() а также __get() магические методы. Это было вызвано тем классом, который у нас есть, который лениво загружает другие объекты через __get() метод (отложенная загрузка означает, что свойство не существует до первого обращения к нему, после чего объект создается и возвращается). У нас были разные мнения о том, что __isset() должен вернуться за свойство, которое еще не загружено, хотя. Технически свойство не существует (оно не установлено или, по крайней мере, установлено, но в настоящее время NULL), но вызов к нему также будет технически успешным (за исключением любых исключений) и вернет неNULL значение.

Итак, мой вопрос, в этой ситуации следует __isset() просто быть индикатором того, или нет __get() будет успешным для того же аргумента (возврат TRUE если __get() будет успешным и вернет неNULL значение). Или, если он будет вести себя более технически, и вернуть FALSE, поскольку данные еще не существуют (даже если они будут доступны при первом обращении)?

Простой пример:

class Foo {
    protected $data;

    public function __get($prop) {
        if ($prop == 'bar') {
            $this->data['bar'] = new Bar;
            return $this->data['bar'];
        }
    }

    public function __isset($prop) {
        if ($prop == 'bar') {
            // What goes here?
            // return isset($this->data[$prop]) would mean
            // that the first call to isset($foo->bar) below will be FALSE
            // which means that using logic like this would always fail
            // and __get() would never be called:
            // isset($foo->bar) ? $foo->bar->baz : 'foo->bar not set'
        }
    }
}

class Bar {}

$foo = new Foo;
var_dump(isset($foo->bar)); // ???
$bar = $foo->bar;
var_dump(isset($foo->bar)); // bool(true)

2 ответа

Решение

Вы должны рассмотреть это с точки зрения пользователя класса. Предположим, что $foo имеет class Fooи у вас есть этот код:

if(isset($foo->bar)) {
    var_dump($foo->bar); // #1
}
else {
    // $foo->bar is "not set", right?
    $x = $foo->bar;
    var_dump($x); // #2
}

Риторические вопросы: ожидаете ли вы № 1 когда-либо печатать null? Вы ожидаете, что #2 напечатает что-нибудь кроме null?

Конечно, нет. Если это работает так, то пользователи class Foo будет проводить большую часть своего рабочего дня, ругая автора.

Предполагая, что я вас убедил, довольно просто начать с желаемого поведения и вернуться к тому, как это должно быть достигнуто, а именно реализовать __isset как это:

public function __isset($prop) {
    $val = $this->$prop;
    return isset($val);
}

Я думаю, что это зависит от того, что isset() проверка означает ваш код. Что использует код isset() на самом деле волнует?

Это просто вопрос свойства является значением (как, в, можно получить доступ)? Затем проверьте, можно ли получить доступ к значению через __get() и вернуть результат.

Это спрашивает, если собственность действительна и не null? Затем на самом деле загрузить значение и вернуть результат.

Это спрашивает, было ли загружено значение? Затем проверьте, если __get() был вызван, и вернуть результат.

Но это должно отражать использование вашего класса, а не какое-то произвольное правило.

Другие вопросы по тегам