PHP - Indirect modification of overloaded property

前端 未结 8 1612
悲哀的现实
悲哀的现实 2020-11-28 08:38

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 map

相关标签:
8条回答
  • 2020-11-28 08:50

    I've had this same error, without your whole code it is difficult to pinpoint exactly how to fix it but it is caused by not having a __set function.

    The way that I have gotten around it in the past is I have done things like this:

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

    now if you do this:

    echo $user->role->rolename;
    

    you should see 'Test'

    0 讨论(0)
  • 2020-11-28 08:51

    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 ;
        }   
    }
    
    0 讨论(0)
  • 2020-11-28 08:51

    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.

    0 讨论(0)
  • 2020-11-28 09:07

    I was receiving this notice for doing this:

    $var = reset($myClass->my_magic_property);
    

    This fixed it:

    $tmp = $myClass->my_magic_property;
    $var = reset($tmp);
    
    0 讨论(0)
  • 2020-11-28 09:10

    I agree with VinnyD that what you need to do is add "&" in front of your __get function, as to make it to return the needed result as a reference:

    public function &__get ( $propertyname )
    

    But be aware of two things:

    1) You should also do

    return &$something;
    

    or you might still be returning a value and not a reference...

    2) Remember that in any case that __get returns a reference this also means that the corresponding __set will NEVER be called; this is because php resolves this by using the reference returned by __get, which is called instead!

    So:

    $var = $object->NonExistentArrayProperty; 
    

    means __get is called and, since __get has &__get and return &$something, $var is now, as intended, a reference to the overloaded property...

    $object->NonExistentArrayProperty = array(); 
    

    works as expected and __set is called as expected...

    But:

    $object->NonExistentArrayProperty[] = $value;
    

    or

    $object->NonExistentArrayProperty["index"] = $value;
    

    works as expected in the sense that the element will be correctly added or modified in the overloaded array property, BUT __set WILL NOT BE CALLED: __get will be called instead!

    These two calls would NOT work if not using &__get and return &$something, but while they do work in this way, they NEVER call __set, but always call __get.

    This is why I decided to return a reference

    return &$something;
    

    when $something is an array(), or when the overloaded property has no special setter method, and instead return a value

    return $something;
    

    when $something is NOT an array or has a special setter function.

    In any case, this was quite tricky to understand properly for me! :)

    0 讨论(0)
  • 2020-11-28 09:12

    I have run into the same problem as w00, but I didn't had the freedom to rewrite the base functionality of the component in which this problem (E_NOTICE) occured. I've been able to fix the issue using an ArrayObject in stead of the basic type array(). This will return an object, which will defaulty be returned by reference.

    0 讨论(0)
提交回复
热议问题