PHP: поведение функции __set каждый раз отличается - PullRequest
4 голосов
/ 22 декабря 2009

Это позволяет создать новое свойство объекта. Но кто-то может объяснить с помощью вспомогательных ссылок, почему setAttrib ведет себя по-разному? Почему это не вызывает ... ожидание этого ... переполнение стека !! ??

class Test
{
  public function setAttrib( $key, $value ) {
    echo "setAttrib\n";

    // first time: calls $this->__set($key, $value)
    // second time: just sets a public property (but, when exactly was it created?)
    $this->$key = $value;
  }

  public function __set( $key, $value ) {
    echo "__set\n";
    $this->setAttrib($key, $value);
  }
}

$test = new Test();
$test->setAttrib('hey', 'It works');
var_dump($test);

производит ...

setAttrib
__set
setAttrib
object(Test)#1 (1) {
  ["hey"]=>
  string(8) "It works"
}

Редактировать: я не ищу альтернативу. Я ищу причину, по которой это работает.

Ответы [ 3 ]

8 голосов
/ 22 декабря 2009

Вы не единственный, кто, кажется, заметил, что нерекурсивное поведение: этот комментарий на странице руководства состояния:

2 - PHP не будет рекурсивно вызывать один магический метод изнутри себя (в хотя бы для того же $name).

И чуть позже на той же странице есть этот , в котором говорится:

Функция обнаружения рекурсии может особенно опасны при использовании __set. Когда PHP сталкивается с заявление, которое обычно вызывает __set но приведет к рекурсии, вместо того, чтобы запустить предупреждение или просто не выполняет заявление это будет действовать, как будто нет __set метод определен вообще.
По умолчанию поведение в этом случае заключается в динамически добавлять указанное свойство к объекту, таким образом нарушая желаемый функционал всего дальнейшего звонки на __set или __get для этого свойство.


И на багтрекер PHP есть # 47215: магический метод __set () обойден при рекурсивном вызове , который говорит:

Магический метод __set() обойден рекурсивный вызов.
PHP автоматически вместо этого создает свойство в экземпляре рекурсивного вызова __set() или вместо того, чтобы бросать рекурсивность ошибка

И он был закрыт как:

Спасибо, что нашли время написать нам, но это не ошибка.

Этот отчет об ошибке сам по себе указывает на это сообщение в блоге , которое заканчивается этим предложением (цитирование, выделение мое) :

В конце концов, я думаю, что это не ошибка но ожидаемое поведение, в противном случае мы не может быть в состоянии определить объект свойства изнутри __set() Метод .

2 голосов
/ 22 декабря 2009

__ set используется только при записи в недоступные свойства. То есть те, кто недоступен (частный или защищенный) или те, которые вообще не установлены. Следовательно, __set будет вызываться только один раз.

Вот что происходит:

  • setAttrib: попытка записи
  • класс: недоступное имущество
  • __set: Делайте все, что велено __set, то есть снова вызовите setAttrib.
  • setAttrib: попытка записи
  • class: недоступное свойство, но __set не может вернуться, и мы уже в нем, поэтому делайте это так, как будто __set не существует.

См. Комментарии пользователей для http://php.net/__set, чтобы доказать, что __set не может рекурсировать.

0 голосов
/ 22 декабря 2009

__set и __get только перехватывают вызов свойства, если член защищен. Это означает, что внутри объекта $this->$key устанавливается свойство $key. если вызывается из области видимости, то вызывается логика __set. Один из ваших вызовов происходит в объекте вне объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...