Как реализовать магический метод __isset() в PHP?

Я пытаюсь заставить такие функции, как empty() и isset(), работать с данными, возвращаемыми методами.

Что у меня есть до сих пор:

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);   
  }

}

Использование:

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

Как я могу сделать так, чтобы пустой/isset возвращал true/false, если:

  • my_stuff выше не установлен или имеет пустое или нулевое значение в случае empty()
  • метод не существует (не уверен, что нужен, потому что я думаю, что вы все равно получите фатальную ошибку)

?


person Alex    schedule 05.06.2011    source источник


Ответы (3)


public function __isset($name){
    $getter = 'get'.ucfirst($name);
    return method_exists($this, $getter) && !is_null($this->$getter());
}

Эта проверка, существует ли $getter() (если оно не существует, предполагается, что свойство также не существует) и возвращает ненулевое значение. Таким образом, NULL приведет к возврату false, как и следовало ожидать после прочтения руководства по php для isset().

person Arjan    schedule 05.06.2011
comment
+1 Коротко и ясно :) Единственное, что следует добавить, это то, что $this->$getter()!==null быстрее, чем !is_null($this->$getter()) :) - person Tadeck; 05.06.2011
comment
Это быстрее, но разница очень мала. Используете ли вы !== null или !is_null() — это дело вкуса, а не оптимизация скорости. - person Arjan; 05.06.2011
comment
Я снова согласен :) Разница небольшая и это больше вопрос вкуса. +1 снова - person Tadeck; 05.06.2011
comment
stackoverflow.com/ вопросы/21227585/ помогут вам - person Lal krishnan S L; 25.02.2014

Еще немного возможности не зависеть от геттера

public function __isset($name)
{
    $getter = 'get' . ucfirst($name);
    if (method_exists($this, $getter)) {
        return !is_null($this->$getter());
    } else {
        return isset($this->$name);
    }
}
person Ansar Ahmed    schedule 18.03.2014
comment
При желании вы можете опустить else для удобочитаемости. (поскольку if возвращается рано, вы можете безопасно это сделать) - person Bert H; 25.04.2018

Ваш код возвращает ошибку из-за этих строк:

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

Вы можете просто заменить его на:

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

и он вернет false, если геттер не определен или вернет NULL. Я предлагаю вам лучше подумать о том, как вы определяете, установлено ли какое-то свойство или нет.

Приведенный выше код также предполагает, что вы создаете геттеры для всех своих свойств, поэтому когда геттер отсутствует, свойство считается неустановленным.

Кроме того, почему вы используете геттеры? Здесь они кажутся излишними.

person Tadeck    schedule 05.06.2011
comment
Если свойство NULL, я бы сказал, что оно не установлено, поэтому isset() правильно возвращает false. И использование геттеров и сеттеров может показаться излишним, но они позволяют рефакторить части приложения, не нарушая других вещей. - person Arjan; 05.06.2011
comment
@Arjan Правильно, NULL заставляет isset() возвращать false. Также теперь я вижу, что у isset($foo->stuff) есть какая-то причина. Короче говоря: потому что $foo->stuff действительно что-то основанное на $foo->my_stuff, но обработанное геттером. - person Tadeck; 05.06.2011
comment
ну, я последовал вашему совету и в итоге использовал getter/setter только для двух переменных, которые действительно в них нуждаются, а для остальных я создал обычные функции get/setVariable() (я использовал их почти для всех приватных переменных) - person Alex; 05.06.2011
comment
@Alex Говоря о геттере или сеттере, я имел в виду переменную, получающую функцию, или переменную, устанавливающую функцию. Итак, теперь вы фактически используете геттеры и сеттеры для всех ваших переменных, если я вас правильно понимаю. Но ладно, если вам нужны геттеры и сеттеры, просто используйте их — я рад, что вы просмотрели свой код и изменили его так, как вы считаете лучше :) - person Tadeck; 05.06.2011
comment
да, я все еще использую геттер/сеттер для всех переменных, но только для двух из них я использую магические методы __set/__get :) Это потому, что эти две переменные являются объектами, и я думаю, что более естественно называть их как $foo->objectvariable->somemethod вместо этого $foo-›getObjectvariable()-›somemethod` - person Alex; 05.06.2011
comment
@Alex Хорошо, теперь я понял твою точку зрения. Что касается вашего подхода - я думаю, вы знаете, что делаете (и, что более важно, знаете, чего хотите достичь) :) - person Tadeck; 05.06.2011