Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP - Indirect modification of overloaded property

I know this question has been asked several times, but none of them have a real answer for a workaround. Maybe there's one for my specific case.

I'm building a mapper class which uses the magic method __get() to lazy load other objects. It looks something like this:

public function __get ( $index ) {     if ( isset ($this->vars[$index]) )     {         return $this->vars[$index];     }      // $index = 'role';     $obj = $this->createNewObject ( $index );      return $obj; } 

In my code I do:

$user = createObject('user'); $user->role->rolename; 

This works so far. The User object doesn't have a property called 'role', so it uses the magic __get() method to create that object and it returns its property from the 'role' object.

But when i try to modify the 'rolename':

$user = createUser(); $user->role->rolename = 'Test'; 

Then it gives me the following error:

Notice: Indirect modification of overloaded property has no effect

Not sure if this is still some bug in PHP or if it's "expected behaviour", but in any case it doesn't work the way I want. This is really a show stopper for me... Because how on earth am I able to change the properties of the lazy loaded objects??


EDIT:

The actual problem only seems to occur when I return an array which contains multiple objects.

I've added an example piece of code which reproduces the problem:

http://codepad.org/T1iPZm9t

You should really run this in your PHP environment the really see the 'error'. But there is something really interesting going on here.

I try to change the property of an object, which gives me the notice 'cant change overloaded property'. But if I echo the property after that I see that it actually DID change the value... Really weird...

like image 972
w00 Avatar asked May 04 '12 19:05

w00


2 Answers

All you need to do is add "&" in front of your __get function to pass it as reference:

public function &__get ( $index ) 

Struggled with this one for a while.

like image 85
VinnyD Avatar answered Sep 25 '22 10:09

VinnyD


Nice you gave me something to play around with

Run

class Sample extends Creator {  }  $a = new Sample (); $a->role->rolename = 'test'; echo  $a->role->rolename , PHP_EOL; $a->role->rolename->am->love->php = 'w00'; echo  $a->role->rolename  , PHP_EOL; echo  $a->role->rolename->am->love->php   , PHP_EOL; 

Output

test test w00 

Class Used

abstract class Creator {     public function __get($name) {         if (! isset ( $this->{$name} )) {             $this->{$name} = new Value ( $name, null );         }         return $this->{$name};     }      public function __set($name, $value) {         $this->{$name} = new Value ( $name, $value );     }    }  class Value extends Creator {     private $name;     private $value;     function __construct($name, $value) {         $this->name = $name;         $this->value = $value;     }      function __toString()     {         return (string) $this->value ;     } }       

Edit : New Array Support as requested

class Sample extends Creator {  }  $a = new Sample (); $a->role = array (         "A",         "B",         "C"  );   $a->role[0]->nice = "OK" ;  print ($a->role[0]->nice  . PHP_EOL);  $a->role[1]->nice->ok = array("foo","bar","die");  print ($a->role[1]->nice->ok[2]  . PHP_EOL);   $a->role[2]->nice->raw = new stdClass(); $a->role[2]->nice->raw->name = "baba" ;  print ($a->role[2]->nice->raw->name. PHP_EOL); 

Output

 Ok die baba 

Modified Class

abstract class Creator {     public function __get($name) {         if (! isset ( $this->{$name} )) {             $this->{$name} = new Value ( $name, null );         }         return $this->{$name};     }      public function __set($name, $value) {         if (is_array ( $value )) {             array_walk ( $value, function (&$item, $key) {                 $item = new Value ( $key, $item );             } );         }         $this->{$name} = $value;      }  }  class Value {     private $name ;     function __construct($name, $value) {         $this->{$name} = $value;         $this->name = $value ;     }      public function __get($name) {         if (! isset ( $this->{$name} )) {             $this->{$name} = new Value ( $name, null );         }          if ($name == $this->name) {             return $this->value;         }          return $this->{$name};     }      public function __set($name, $value) {         if (is_array ( $value )) {             array_walk ( $value, function (&$item, $key) {                 $item = new Value ( $key, $item );             } );         }         $this->{$name} = $value;     }      public function __toString() {         return (string) $this->name ;     }    } 
like image 43
Baba Avatar answered Sep 22 '22 10:09

Baba