0

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.

John Smith
  • 6,129
  • 12
  • 68
  • 123
  • 2
    `public function set(\stdClass &$t)` <-- do not use ampersand here. Non-scalars like objects are always passed by reference. So if you pass an object around, it is **not cloned**. – Markus Zeller Nov 19 '22 at 18:23
  • 1
    I'm not sure what you're actually looking for. The abstraction in the question is quiet high, and it feels like there must be a better way of achieving what you're trying to achieve. You might need a Builder, Factory, or Singleton but working with references like that seems weird to me... However there might be a good reason that's not clear from the code examples. Is there only a single `t` / `StdClass` object present? Might this be helpful: https://onlinephp.io/c/08d10 ? – SvenTUM Nov 25 '22 at 12:17
  • 1
    I have just read this answer https://stackoverflow.com/questions/2209934/php-operator#answer-19067966 and it seems to be related to what you are asking. – Lenny4 Nov 28 '22 at 15:25

2 Answers2

3

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)
  }
}
Lenny4
  • 1,215
  • 1
  • 14
  • 33
2

I'm not 100% on this, but if I understand your question correctly I believe your code actually does what you expect it to do. However creating a reference is not nessecary.

There is only one reference to the stdClass object in this case. Therefor the representation with the ampersand is not nessesary.

If you add another reference is should appear:

<?php
class TestClass
{
    public function __get(string $propertyName)
    {
        $xx = new StdClass();
        $xx->fromGET = 1;
        $this->t = $xx; // no reference needed here
        return $this->t;
    }
}

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

// another Reference to the object is created
$t =& $test->t;
var_dump($test);
SvenTUM
  • 784
  • 1
  • 6
  • 16
  • I know references goes around and changing a property of object will change the object everywhere - but what if the object itself changes? I will update my answer – John Smith Nov 22 '22 at 14:23