开发者

PHP: __set function behaviour different each time

This manages to create a new property on the object. But, can someone explain, with supporting links, why setAttrib behaves in two different ways? Why doesn't it cause a... wait for it... stack overflow!!??

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

produces...

setAttrib
__set
setAttrib
object(Test)#1 (1) {
  ["开发者_StackOverflowhey"]=>
  string(8) "It works"
}

Edit: I'm not looking for an alternative. I'm looking for the reason why this works.


You are not the only one who seems to have notice that non-recursive behaviour : this comment on the manual's page states :

2 - PHP will not recursively call one magic method from within itself (at least for the same $name).

And, a bit later on the same page, there is this one, which states :

The recursion detection feature can prove especially perilous when using __set. When PHP comes across a statement that would usually call __set but would lead to recursion, rather than firing off a warning or simply not executing the statement it will act as though there is no __set method defined at all.
The default behaviour in this instance is to dynamically add the specified property to the object thus breaking the desired functionality of all further calls to __set or __get for that property.


And, on PHP's bugtracker, there is #47215 : magic method __set() is bypassed on recursive call, which says :

Magic method __set() is bypassed on recursive call.
PHP automatically creates a property on instance instead of recursively calling __set() or instead of throwing a recursivity error

And it has been closed as :

Thank you for taking the time to write to us, but this is not a bug.

That bug-report, itself, points to this blog-post, which ends by this sentence (quoting, emphasis mine) :

After all I think it may not be a bug but expected behaviour, otherwise we could not be able to define object properties from within __set() method.


__set is only used when writing to inaccessible properties. That is, those who are not accessible (private or protected) or those that aren't set at all. Therefore, __set will only be called once.

Here's what happens:

  • setAttrib: Attempt to write
  • class: inaccessible property
  • __set: Do whatever __set is told to do, which is call setAttrib again.
  • setAttrib: Attempt to write
  • class: inaccessible property, but __set can't recurse, and we're already in it, so do it as if __set didn't exist.

See user comments for http://php.net/__set for proof that __set can't recurse.


__set and __get only catch the call for the property if the member is protected. meaning that within the object $this->$key sets the property $key. if called from out of scope then the __set logic is called. One of your calls happens in the object outside the object.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜