Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Visibility with Magic Methods __get & __set

I recently went to an interview and my code I supplied had magic functions to get and set 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 the interview the guy asked me about the visibility on my variables, I had private ones set but these were now accessible by using magic functions. Essentially I failed the interview on this point, so I wanted to understand more. I was following a tutorial from PHP Master and found a different __get, I have tried to break it but it seems to work, but in a strange way.

I call __get('test') to get my variable _test but if it is set to private it calls itself again and tells me that it cannot access __test. I do not really understand why it calls itself 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 anyone give me some pointers on proper use of __get and __set when using visibility in a class and why this function would call itself again.

I have read the other posts here but I am still struggling with this concept.

like image 340
Charles Bryant Avatar asked Mar 21 '12 11:03

Charles Bryant


People also ask

What is __ method __ in PHP?

"Method" is basically just the name for a function within a class (or class function). Therefore __METHOD__ consists of the class name and the function name called ( dog::name ), while __FUNCTION__ only gives you the name of the function without any reference to the class it might be in.

What is __ get in PHP?

PHP calls the __get() method automatically when you access a non-existing or inaccessible property. PHP calls the __set() method automatically when you assign a value to a non-existing or inaccessible property.

What is __ call () in PHP?

When you call a method on an object of the Str class and that method doesn't exist e.g., length() , PHP will invoke the __call() method. The __call() method will raise a BadMethodCallException if the method is not supported. Otherwise, it'll add the string to the argument list before calling the corresponding function.


1 Answers

I just bumped into this question and there is a little thing that may be worth clarifying:

I do not really understand why it calls itself again.

The code is not calling itself again but trying to execute a custom getter if there is one defined. Let me break down the method execution:

public function __get($name)
{

As already explained in the other answers and here, the __get() magic method is called when you are trying to access a property that is not declared or not visible in the calling scope.

$field = '_' . strtolower($name);

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

Here it just checks that a property with an underscore pre-appended exists in the class definition. If it doesn't, an exception is thrown.

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

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

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

The final part is a bit cryiptic, since many things are happening in one line:

  • method_exists($this, $accessor). Checks if the receiver ($this) has a method with $accessor name (in our example, getEmail).
  • is_callable(array($this, $accessor)). Checks that the getter can be called.
  • If both conditions are met, the custom getter is called and its return value is returned ($this->$accessor()). If not, the property contents 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

HTH

like image 90
Andrés Fortier Avatar answered Sep 27 '22 18:09

Andrés Fortier