How does PHP avoid infinite recursion here?

Go To StackoverFlow.com

12

Consider this class:

class test
{
    public function __set($n, $v)
    {
        echo "__set() called\n";
        $this->other_set($n, $v, true);
    }

    public function other_set($name, $value)
    {
        echo "other_set() called\n";    
        $this->$name = $value;
    }

    public function t()
    {
        $this->t = true;
    }
}

I am overloading PHP's magic __set() method. Whenever I set a property in an object of the test class, it will call __set(), which in turn calls other_set().

$obj = new test;
$test->prop = 10;

/* prints the following */
__set() called
other_set() called

But other_set() has the following line $this->$name = $value. Shouldn't this result in a call to __set(), causing infinite recursion?

I theorized that it would call __set() only when setting things outside the class. But if you call the method t() you can see it clearly goes through __set() too.

2012-04-04 19:13
by Confluence


11

__set is only called once per attempt for a given property name. If it (or anything it calls) attempts to set the same property, PHP won't call __set again -- it'll just set the property on the object.

2012-04-04 19:24
by cHao
This is correct. If anyone wants to see the implementation details, it's in zend_object_handlers.c - Confluence 2012-04-04 20:14
@Confluence: Thanks...i was wondering where i'd read that. :) Couldn't find it in the manual; i was starting to wonder if i'd imagined it. But all my tests confirmed it, so.. - cHao 2012-04-04 20:38
This answer was SO useful - Valentin Mercier 2014-08-18 21:25


2

From the documentation:

__set() is run when writing data to inaccessible properties

For example:

class foo {
  private $attributes;
  public $bar;

  public function __construct() {
    $this->attributes = array();
  }

  public function __set($n, $v) {
    echo "__set() called\n";
    $this->attributes[$n] = $v;
  }
}

$x = new foo;
$x->prop = "value";
$x->attributes = "value";
$x->bar = "hello world";

In this case, $x->prop is inaccessible and __set will be called. $x->attributes is also inaccessible, so __set will be called. However, $x->bar is publicly accessible, so __set will not be called.

Similarly, in the __set method, $this->attribtues is accessible, so there is no recursion.

In your example code above, $this->$name is accessible in the scope in which its called, therefore __set is not called.

2012-04-04 19:19
by thetaiko
What is difference does that make - Confluence 2012-04-04 19:20
@Confluence the property isn't inaccessible from within the class itself. - TZHX 2012-04-04 19:26
@TZHX Inaccessible can mean not visible OR undeclared. All properties are inaccessible by definition since I have no declared properties - Confluence 2012-04-04 19:28
@TZHX: The property doesn't exist -- it's "inaccessible" til it's set, even within the class - cHao 2012-04-04 19:29
In the context in which its called, it is accessible - thetaiko 2012-04-04 19:35
@thetaiko You are wrong. $this->$name is not accessible until I set the property. It only becomes accessible after it's set. You can easily see that if you try to change $x->prop after it's been set - Confluence 2012-04-04 19:35
@Confluence - indeed, you're correct. But in that case, cHao is right - __set will not call itself again - thetaiko 2012-04-04 19:41
Ads