Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does PHP avoid infinite recursion here?

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.

like image 645
Confluence Avatar asked Apr 04 '12 19:04

Confluence


2 Answers

__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.

like image 172
cHao Avatar answered Sep 27 '22 22:09

cHao


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.

like image 34
thetaiko Avatar answered Sep 27 '22 23:09

thetaiko