PHP Visibility with __get & __set Magic Methods

I recently went for an interview, and my code I provided had magic functions for getting and setting variables. My code was as follows:

public function __get($name){ try { return $this->$name; } catch (Exception $e) { throw new Exception('Trying to get a variable "'.$name.'" that does not exist.'); } } 

In an interview, the guy asked me about the visibility of my variables, I had private ones, but now they were accessible using magic functions. In fact, I failed in an interview on this subject, so I wanted to understand more. I followed the tutorial from PHP Master and found another __get , I tried to break it, but it works, but in a weird way.

I call __get('test') to get my _test variable, but if it is set to private, it calls itself again and tells me that it cannot access __test . I really don't understand why he calls himself again.

 public function __get($name) { $field = '_' . strtolower($name); if (!property_exists($this, $field)){ throw new \InvalidArgumentException( "Getting the field '$field' is not valid for this entity" ); } $accessor = 'get' . ucfirst(strtolower($name)); return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ? $this->$accessor() : $this->$field; } 

Can someone give me some pointers to the correct use of __get and __set when using visibility in the class and why this function will call itself again.

I read other posts here, but I'm still struggling with this concept.

+4
source share
5 answers

I just came across this question, and there is something that may be useful to clarify:

I don’t understand why he calls himself again.

The code does not call itself again, but tries to execute a custom getter, if defined. Let me break up the execution of a method:

 public function __get($name) { 

As already explained in other answers and here , the __get () magic method is called when you try to access a property that is not declared or invisible in the call area.

 $field = '_' . strtolower($name); if (!property_exists($this, $field)){ throw new \InvalidArgumentException( "Getting the field '$field' is not valid for this entity" ); } 

Here, it simply verifies that a property with a preliminary underscore exists in the class definition. If this is not the case, an exception is thrown.

 $accessor = 'get' . ucfirst(strtolower($name)); 

Here it creates the name of the recipient to call if it exists. Thus, if you try to access a property named email , and there is a private member named _email , the $accessor variable will now contain the string 'getEmail' .

 return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ? $this->$accessor() : $this->$field; 

The last part is a bit critical, as many things happen on one line:

  • method_exists($this, $accessor) . Checks if the receiver ( $this ) has a method called $accessor (in our example getEmail ).
  • is_callable(array($this, $accessor)) . Checks what getter can be called .
  • If both conditions are met, a custom getter is called and its return value is returned ( $this->$accessor() ). If not, the contents of the property are returned ( $this->$field ).

As an example, consider this class definition:

 class AccessorsExample { private $_test1 = "One"; private $_test2 = "Two"; public function getTest2() { echo "Calling the getter\n"; return $this->_test2; } public function __get($name) { $field = '_' . strtolower($name); if (!property_exists($this, $field)){ throw new \InvalidArgumentException( "Getting the field '$field' is not valid for this entity" ); } $accessor = 'get' . ucfirst(strtolower($name)); return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ? $this->$accessor() : $this->$field; } } 

and then run:

 $example = new AccessorsExample(); echo $example->test1 . "\n"; echo $example->test2 . "\n"; 

You should see:

 One Calling the getter Two 

NTN

+4
source

I find it clearer when I allow access to properties through __get() . That way, you can still have truly private members, and you don't run the risk of accidentally exposing things that you add later.

 class Foo { // readonly private $foo; private $bar; // truly private private $baz; public function __get($var) { switch ($var) { // readonly access to foo and bar, but not baz case 'foo': case 'bar': return $this->$var; // readonly dynamically generated property case 'buzz': return $this->buzz(); default: throw new InvalidPropertyException($var); } } public function __isset($var) { switch ($var) { // return true for foo, bar and buzz so functions like isset() // and empty() work as expected case 'foo': case 'bar': case 'buzz': return true; default: return false; } } // dynamic readonly property implementation private function buzz() { // calculate and return something which depends on other private properties } } 
+1
source

I don’t understand what your problem is, but for example this code works

 <?php class foo { private $_test = "my"; public function __get($name) { $field = '_' . strtolower($name); if (!property_exists($this, $field)){ throw new InvalidArgumentException( "Getting the field '$field' is not valid for this entity" ); } $accessor = 'get' . ucfirst(strtolower($name)); return (method_exists($this, $accessor) && is_callable(array($this, $accessor))) ? $this->$accessor() : $this->$field; } } $foo = new foo(); echo $foo->test; 

as you can check here (http://codepad.org/jmkvHiDe).

Magic methods such as __get() will be called when you try to access a private property exactly as they will be called to access a non-existing property, of course, if you set the property to "private" and then the user can access to a variable using the magic method, why first set the property as private?

0
source

Instead of $this->$name;

Use something like $this->protected_values[$name];

0
source
 public function __get($name){ try { return $this->$name; } catch (Exception $e) { throw new Exception('Trying to get a variable "'.$name.'" that does not exist.'); } } 

A few problems with this method in its current form, without seeing the rest of the code:

  • It allows unrestricted public access to read all private and protected properties within the class. Except in special cases, this is usually undesirable, since it affects the object of visibility of a member of the class. As mentioned earlier, access should be restricted either by checking the allowed list (as in Rob Agar's answer) or by checking the specific recipient (as in the OP question).

  • An exception usually does not occur when accessing the undefined property (unless you have a custom error handler that is installed for this). Access to the undefined property usually starts E_NOTICE, so your method will not interfere with this. You must first confirm that $name really exists.

0
source

Source: https://habr.com/ru/post/1402631/


All Articles