phpvisibilitymagic-methodszend-certification

PHP variable visibility and behaviour with magic method


This question is related to: PHP magic methods example

I've asked this question in comment in accepted answer but I guess, it has not been noticed so I have to create this question.

<?php
    class Magic {
        public $a = "A";
        protected $b = array(
            "a"=>"A", 
            "b"=>"B", 
            "c"=>"C"
        );
        protected $c = array(1,2,3);

        public function __get($v) {
            echo "$v,";
            return $this->b[$v];
        }

        public function __set($var, $val) {
            echo "$var: $val,";
            $this->$var = $val;
        }
    }

    $m = new Magic();
    echo $m->a.",".$m->b.",".$m->c.",";
    $m->c = "CC";
    echo $m->a.",".$m->b.",".$m->c;
?>

Output: b,c,A,B,C,c: CC,b,c,A,B,C

$m->c = "CC"; Here we already have protected variable with same name. So, how should this should behave in context of visibility?

If it overwrites value of protected variable c, then isn't it a loop hole for protected/private variables? (I guess that would not be the case)

If not then, the statement: $this->$var = $val; seems to create public variable with same name already defined as protected. Is that possible?

Also, after this statement: $m->c = "CC";, when we access $m->c again, PHP calls __get again as if c has no public visibility. Does that mean $this->$var = $val; has no life time for immediate next statement? (I guess that would also not be the case)

Can anybody please explain, it should behaves in such cases and how it gave such output?


Solution

  • It looks like c is being changed by the methods only. While it is protected, the magic methods expose it.

    If you look at this example:

    class Magic
    {
        protected $b = 'B';
    
        public function __get($v)
        {
            return 'C';
        }
    
        public function __set($v, $val)
        {
            $this->$v = $val;
        }
    }
    
    $magic->b = 'D';
    echo $magic->b; // Outputs: C
    

    The first call does set property b to D, however because the getter is hard-coded to return C, the protected access modifier is still respected.

    EDIT.

    Another use of magic getters/setters is accessing collections. This example demonstrates their flexibility:

    class Magic
    {
        private $properties = array();
    
        public function __get($key)
        {
            if(isset($this->properties[$key])) {
                return $this->properties[$key];
            }
        }
    
        public function __set($key, $value)
        {
            $this->properties[$key] = $value;
        }
    
        public function dump($return = false)
        {
            if($return) {
                return print_r($this->properties, true);
            } else {
                print_r($this->properties);
            }
        }
    }
    
    $magic = new Magic();
    
    $magic->a = '123';
    $magic->b = '456';
    $magic->c = '789';
    
    echo '<pre>';
    
    echo sprintf('A: %s%s', $magic->a, PHP_EOL);
    echo sprintf('B: %s%s', $magic->b, PHP_EOL);
    echo sprintf('C: %s%s', $magic->c, PHP_EOL);
    
    echo PHP_EOL;
    
    echo $magic->dump(true);
    

    Output:

    A: 123
    B: 456
    C: 789
    
    Array
    (
        [a] => 123
        [b] => 456
        [c] => 789
    )
    

    They are only functions and therefore follow the same rules, only difference is the method in which they are invoked.