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.
"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.
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.
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.
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.$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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With