Search code examples
phpgetter-setter

php, I cant set property as reference from __get, any workaround?


normally we would use it like that:

class TestClass
{
    public $t;
    public function set(\stdClass &$t)
    {
        $this->t = &$t;
    }
}

$obj = new \stdClass();
$obj->fromOUTSIDE = 1;
$test = new TestClass();
$test->set($obj);
var_dump($test);

https://onlinephp.io/c/cf4e3

this results in the desired result:

object(TestClass)#2 (1) {
  ["t"]=>
  &object(stdClass)#1 (1) {
    ["fromOUTSIDE"]=>
    int(1)
  }
}

notice the & character, as its a reference. So far so good!

But what if the __get magic method creates this?

class TestClass
{
    public function __get(string $propertyName)
    {
        $xx = new \stdClass();
        $xx->fromGET = 1;
        $this->t = &$xx;
        return $this->t;
    }
}

$test = new TestClass();
$test->t;
var_dump($test);

https://onlinephp.io/c/21f4f

the reference character disappeared!

object(TestClass)#1 (1) {
  ["t"]=>
  object(stdClass)#2 (1) {
    ["fromGET"]=>
    int(1)
  }
}

how to make it referenced? Even using the public function &__get form still no work!

EDIT:

So a basic code:

class X
{
    public \stdClass $t;
    
    public function __construct(\stdClass &$t)
    {
        $this->t = &$t;
    }
}


$t = new \stdClass();
$t->TTTT = 1;

$X = new X($t);
var_dump($t);echo "\r\n";
var_dump($X->t);echo "\r\n";

$t = new \stdClass();
$t->TTTT = 2;

var_dump($t);echo "\r\n";
var_dump($X->t);echo "\r\n";

https://onlinephp.io/c/9cd7a

see, it results #1, #1, #3, #1 because renewing the old object wont be affected the object inside the X. If I do:

<?php
class X
{
    public \stdClass $t;
    
    public function __construct(\stdClass &$t)
    {
        $this->t = &$t;
    }
}


$t = new \stdClass();
$t->TTTT = 1;

$X = new X($t);
var_dump($t);echo "\r\n";
var_dump($X->t);echo "\r\n";

$t = new \stdClass();
$t->TTTT = 2;

var_dump($t);echo "\r\n";
var_dump($X->t);echo "\r\n";

https://onlinephp.io/c/8efd4

gives the desired result, #1, #1, #3, #3. But what if $t property doesn't exist? Maybe __get has to create it or obtain from an object-container. And this is where I can't solve it.


Solution

  • Your t property will not be a reference because you declare your stdClass inside the __get function. Which means that your $xx will be destroy when the execution of the __get function is finished.

    How can you do it so ?

    2 options:

    1. Create a global variable:
    <?php
    
    class TestClass
    {
        public function __get(string $propertyName)
        {
            global $xx;
            $xx = new \stdClass();
            $xx->fromGET = 1;
            $this->t = &$xx;
            return $this->t;
        }
    }
    
    $test = new TestClass();
    $test->t;
    var_dump($test);
    

    Result (https://onlinephp.io/c/c9b92):

    object(TestClass)#1 (1) {
      ["t"]=>
      &object(stdClass)#2 (1) {
        ["fromGET"]=>
        int(1)
      }
    }
    
    1. Declare $xx outside the __get function:
    <?php
    
    class TestClass
    {
        public function __construct(\stdClass $xx)
        {
            $this->xx = $xx;
        }
    
        public function __get(string $propertyName)
        {
            $this->xx->fromGET = 1;
            $this->t = &$this->xx;
            return $this->t;
        }
    }
    
    $test = new TestClass(new \stdClass());
    $test->t;
    var_dump($test);
    

    Result (https://onlinephp.io/c/a99f3):

    object(TestClass)#1 (2) {
      ["xx"]=>
      &object(stdClass)#2 (1) {
        ["fromGET"]=>
        int(1)
      }
      ["t"]=>
      &object(stdClass)#2 (1) {
        ["fromGET"]=>
        int(1)
      }
    }
    

    Futhermore if you change option 2 and add unset($test->xx), you will see that you loose the reference as in your example.

    <?php
    
    class TestClass
    {
        public function __construct(\stdClass $xx)
        {
            $this->xx = $xx;
        }
    
        public function __get(string $propertyName)
        {
            $this->xx->fromGET = 1;
            $this->t = &$this->xx;
            return $this->t;
        }
    }
    
    $test = new TestClass(new \stdClass());
    $test->t;
    unset($test->xx);
    var_dump($test);
    

    Result (https://onlinephp.io/c/801b8):

    object(TestClass)#1 (1) {
      ["t"]=>
      object(stdClass)#2 (1) {
        ["fromGET"]=>
        int(1)
      }
    }