I recently had a short discussion with another developer about the relationship between PHP’s __isset() and __get() magic methods. It was brought about by a class that we have which lazy-loads other objects via the __get() method (lazy-loading meaning that the property doesn’t exist until first accessed, at which point the object is instantiated and returned). We had differing opinions on what __isset() should return for a property that has yet to be loaded, though. The property technically doesn’t exist (it isn’t set, or at the very least, it’s set but currently NULL), but a call to it will also technically succeed (barring any exceptions) and return a non-NULL value.
So, my question is, in this situation should __isset() simply be an indicator of whether or not __get() will succeed for the same argument (return TRUE if __get() will succeed and return a non-NULL value). Or, should it behave more technically, and return FALSE, since the data does not yet exist (even though it will when first accessed)?
A simple example:
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)
You have to consider this from the perspective of a user of the class. Assume that
$foois ofclass Foo, and you have this code:Rhetorical questions: would you expect #1 to ever print
null? Would you expect #2 to print anything exceptnull?Of course not. If it worked like that, then users of
class Foowould spend most of their working day cursing the author.Assuming that I have convinced you, it’s quite simple to start from the desired behavior and work back to how it should be achieved, namely to implement
__issetlike this: