How to implement __isset() magic method in PHP?
Asked Answered
A

3

13

I'm trying to make functions like empty() and isset() work with data returned by methods.

What I have so far:

abstract class FooBase{

  public function __isset($name){
    $getter = 'get'.ucfirst($name);
    if(method_exists($this, $getter))
      return isset($this->$getter()); // not working :(
      // Fatal error: Can't use method return value in write context 
  }

  public function __get($name){
    $getter = 'get'.ucfirst($name);
    if(method_exists($this, $getter))
      return $this->$getter();
  }

  public function __set($name, $value){
    $setter = 'set'.ucfirst($name);
    if(method_exists($this, $setter))
      return $this->$setter($value);
  }

  public function __call($name, $arguments){
    $caller = 'call'.ucfirst($name);
    if(method_exists($this, $caller)) return $this->$caller($arguments);   
  }

}

the usage:

class Foo extends FooBase{
  private $my_stuff;

  public function getStuff(){
    return $this->my_stuff;
  }

  public function setStuff($stuff){
    $this->my_stuff = $stuff;
  }
}


$foo = new Foo();

if(empty($foo->stuff)) echo "empty() works! \n"; else "empty() doesn't work:( \n";
$foo->stuff = 'something';
if(empty($foo->stuff)) echo "empty() doesn't work:( \n"; else "empty() works! \n";

http://codepad.org/QuPNLYXP

How can I make it so empty/isset return true/false if:

  • my_stuff above is not set, or has a empty or zero value in case of empty()
  • the method doesn't exist (not sure if neeed, because I think you get a fatal error anyway)

?

Anglophobia answered 5/6, 2011 at 11:23 Comment(3)
isset(..) only works on a value, not on a function that returns a value. See the notes section: php.net/manual/en/function.isset.phpSpringlet
Don't use magic __get and __set if you have normal getters and setters. Accessors exists exactly to avoid using public properties (because it's violation of encapsulation).Retaliation
#21228085Periodicity
P
10
public function __isset($name){
    $getter = 'get'.ucfirst($name);
    return method_exists($this, $getter) && !is_null($this->$getter());
}

This check whether or not $getter() exists (if it does not exist, it's assumed that the property also does not exist) and returns a non-null value. So NULL will cause it to return false, as you would expect after reading the php manual for isset().

Purslane answered 5/6, 2011 at 12:2 Comment(4)
+1 Short and clean :) Only thing that should be added is that $this->$getter()!==null is faster than !is_null($this->$getter()) :)Lamkin
It's faster, but the difference is very small. Whether you use !== null or !is_null() is a matter of taste, not speed optimization.Purslane
I agree again :) The difference is small and it is more a question of taste. +1 againLamkin
#21228085 will help youPeriodicity
T
3

A bit more option not to depend on getter

public function __isset($name)
{
    $getter = 'get' . ucfirst($name);
    if (method_exists($this, $getter)) {
        return !is_null($this->$getter());
    } else {
        return isset($this->$name);
    }
}
Thelmathem answered 18/3, 2014 at 16:12 Comment(1)
If you like, you can omit the else for readability. (since the if is returning early, you could safely do that)Dhole
L
1

Your code returns error because of these lines:

if(method_exists($this, $getter))
return isset($this->$getter());

You can just replace it with:

if (!method_exists($this), $getter) {
    return false; // method does not exist, assume no property
}
$getter_result = $this->$getter();
return isset($getter_result);

and it will return false if the getter is not defined or it returns NULL. I propose you should better think of the way you determine some property is set or not.

The above code is also assuming that you are creating getters for all of your properties, thus when there is no getter, the property is assumed as not set.

Also, why are you using getters? They seem to be some overkill here.

Lamkin answered 5/6, 2011 at 11:28 Comment(6)
If a property is NULL I would say it is not set, so isset() correctly returns false. And using getters and setters may seem overkill, but they do allow to refactor parts of the application without breaking other things.Purslane
@Purslane Correct, NULL makes isset() return false. Also now I see that isset($foo->stuff) has some reason. To be short: because $foo->stuff is really something based on $foo->my_stuff, but processed by getter.Lamkin
well I followed your advice and ended up using getter/setter only for two variables which really need them, and for the rest I created normal get/setVariable() functions (I was using them for almost all private varibles)Anglophobia
@Anglophobia By saying "getter" or "setter", I meant function getting variable or function setting variable. So now you actually use getters & setters for all of your variables, if I understand you correctly. But ok, if you need getters & setters, just use them - I am happy you walked through your code and redesigned it in a way you think is better now :)Lamkin
yes, I still use getter/setter for all variables, but only for two of them I'm using the __set/__get magic methods :) This is because those two variables are objects and I think it's more natural to call them like $foo->objectvariable->somemethod instead of $foo->getObjectvariable()->somemethod`Anglophobia
@Anglophobia Ok, now I get your point. As far as your approach is concerned - I think you know what you are doing (and - more importantly - you know what you want to achieve) :)Lamkin

© 2022 - 2024 — McMap. All rights reserved.