Видимость PHP с помощью магических методов __get & __set - PullRequest
5 голосов
/ 21 марта 2012

Недавно я пошел на собеседование, и в моем коде были волшебные функции для получения и установки переменных.Мой код был следующим:

public function __get($name){
    try { 
        return $this->$name;
    } catch (Exception $e) { 
        throw new Exception('Trying to get a variable "'.$name.'" that does not exist.'); 
    }
}

В интервью парень спросил меня о видимости моих переменных, у меня были установлены частные, но теперь они были доступны с помощью магических функций.По сути, я провалил собеседование по этому вопросу, поэтому я хотел понять больше.Я читал учебник по PHP Master и нашел другой __get, я пытался его сломать, но он работает, но странным образом.

Я вызываю __get('test'), чтобы получить мою переменную _test, но если для него установлено значение private, он снова вызывает себя и сообщает, что не может получить доступ к __test.Я не очень понимаю, почему он снова вызывает себя.

public function __get($name)
{
    $field = '_' . strtolower($name);

    if (!property_exists($this, $field)){
        throw new \InvalidArgumentException(
            "Getting the field '$field' is not valid for this entity"
        );
    }

    $accessor = 'get' . ucfirst(strtolower($name));
    return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ?
        $this->$accessor() : $this->$field;

}

Может кто-нибудь дать мне несколько советов о правильном использовании __get и __set при использовании видимости в классе и почему эта функция будет вызывать себя снова.

Я прочитал другие посты здесь, но я все еще борюсь с этой концепцией.

Ответы [ 5 ]

5 голосов
/ 18 декабря 2012

Я только что натолкнулся на этот вопрос, и есть кое-что, что, возможно, стоит уточнить:

Я не очень понимаю, почему он снова вызывает себя.Код не вызывает себя снова, но пытается выполнить пользовательский метод получения, если он определен.Позвольте мне разбить выполнение метода:

public function __get($name)
{

Как уже объяснялось в других ответах и ​​ здесь , магический метод __get () вызывается, когда вы пытаетесь получить доступ к свойству, котороене объявляется или не отображается в вызывающей области.

$field = '_' . strtolower($name);

if (!property_exists($this, $field)){
    throw new \InvalidArgumentException(
        "Getting the field '$field' is not valid for this entity"
    );
}

Здесь он просто проверяет, существует ли свойство с предварительно добавленным подчеркиванием в определении класса.Если это не так, генерируется исключение.

$accessor = 'get' . ucfirst(strtolower($name));

Здесь он создает имя получателя для вызова, если он существует.Таким образом, если вы попытаетесь получить доступ к свойству с именем email и существует закрытый член с именем _email, переменная $accessor теперь будет содержать строку 'getEmail'.

return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ?
    $this->$accessor() : $this->$field;

Последняя частьнемного загадочно, так как в одной строке происходит много вещей:

  • method_exists($this, $accessor).Проверяет, есть ли у получателя ($this) метод с именем $accessor (в нашем примере getEmail).
  • is_callable(array($this, $accessor)).Проверяет, что получатель может быть вызван .
  • Если выполняются оба условия, вызывается пользовательский получатель и возвращается его возвращаемое значение ($this->$accessor()).Если нет, возвращается содержимое свойства ($this->$field).

В качестве примера рассмотрим определение этого класса:

class AccessorsExample
{
private $_test1 = "One";
private $_test2 = "Two";

public function getTest2()
{
echo "Calling the getter\n";
return $this->_test2;
}

public function __get($name)
{
    $field = '_' . strtolower($name);

    if (!property_exists($this, $field)){
        throw new \InvalidArgumentException(
            "Getting the field '$field' is not valid for this entity"
        );
    }

    $accessor = 'get' . ucfirst(strtolower($name));
    return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ?
        $this->$accessor() : $this->$field;

}
}

и затем запустите:

$example = new AccessorsExample();
echo $example->test1 . "\n";
echo $example->test2 . "\n";

Вы должны увидеть:

One
Calling the getter
Two

HTH

1 голос
/ 21 марта 2012

Я считаю, что лучше быть явным при разрешении доступа к свойствам через __get().Таким образом, у вас все еще могут быть по-настоящему приватные участники, и вы не рискуете случайно разоблачить то, что добавляете позже.

class Foo
{
  // readonly 
  private $foo;
  private $bar;

  // truly private
  private $baz;

  public function __get($var)
  {
    switch ($var)
    {
      // readonly access to foo and bar, but not baz 
      case 'foo':
      case 'bar':
        return $this->$var;

      // readonly dynamically generated property
      case 'buzz':
        return $this->buzz();


      default: 
        throw new InvalidPropertyException($var);
    }
  }

  public function __isset($var)
  {
    switch ($var)
    {
      // return true for foo, bar and buzz so functions like isset() 
      // and empty() work as expected
      case 'foo':
      case 'bar':
      case 'buzz':
        return true;

      default: 
        return false;
    }
  }

  // dynamic readonly property implementation
  private function buzz()
  {
    // calculate and return something which depends on other private properties
  }
}
0 голосов
/ 29 января 2014
public function __get($name){
    try { 
        return $this->$name;
    } catch (Exception $e) { 
        throw new Exception('Trying to get a variable "'.$name.'" that does not exist.'); 
    }
}

Пара проблем с этим методом, как он есть, не видя остальной код:

  • Позволяет неограниченный публичный доступ для чтения к всем закрытым и защищенным свойствам внутри класса. За исключением особых случаев это, как правило, нежелательно, так как это наносит ущерб объекту видимости члена класса. Как упоминалось ранее, доступ должен быть ограничен либо путем проверки по разрешенному списку (как в ответе Роба Агара), либо путем проверки определенного получателя (как в вопросе ОП).

  • Обычно исключение не выдается при доступе к неопределенному свойству (если только у вас не установлен специальный обработчик ошибок). Доступ к неопределенному свойству обычно вызывает E_NOTICE, поэтому ваш метод не может перехватить это. Сначала вы должны подтвердить, что $name действительно существует.

0 голосов
/ 21 марта 2012

Вместо $this->$name;

Используйте что-то вроде $this->protected_values[$name];

0 голосов
/ 21 марта 2012

Я не понимаю, в чем ваша проблема, но, например, этот код работает

<?php

class foo {

private $_test = "my";

public function __get($name)
{
    $field = '_' . strtolower($name);

    if (!property_exists($this, $field)){
        throw new InvalidArgumentException(
            "Getting the field '$field' is not valid for this entity"
        );
    }

    $accessor = 'get' . ucfirst(strtolower($name));
    return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ?
        $this->$accessor() : $this->$field;

}
}

$foo = new foo();

echo $foo->test;

как вы можете проверить здесь (http://codepad.org/jmkvHiDe).

Будут вызваны такие магические методы, как __get(), когда вы попытаетесь получить доступ к частному свойству точно так же, как они будут вызваны для доступа к несуществующему свойству, конечно, если вы установите свойство как «личное», а затем Получите доступ к переменной с помощью магического метода. Зачем сначала устанавливать свойство как личное?

...