Question

I have two examples of a simple class using the __set() and __get() magic methods. One throws a fatal error and the other does not when attempting to access a protected property with the unset() function.

In Example 1, I'm naming my protected property starting with an underscore and allowing access via the friendly name and prepending the underscore in my __set() and __get() methods. (Effectively exposing the property without an underscore).

In Example 2, I'm not starting the name with an underscore and allowing access via the name directly in the __set() and __get() methods.

Questions

1) Why does Example 1 not throw a fatal error while Example 2 does throw a fatal error? I would expect either both to throw the error or neither to throw the error.

2) Also, why does Example 1 not actually unset the property? I would expect the property to not contain a value after the unset() function is called.

Example 1

class Example {

    protected $_my_property;

    function __get($name) {
        echo '<h4>__get() was triggered!</h4>';
        $name = '_' . $name;
        if (property_exists($this, $name)) {
            return $this->$name;
        }
        else {
            trigger_error("Undefined property in __get(): $name");
            return NULL;
        }
    }

    function __set($name, $value) {
        echo '<h4>__set() was triggered!</h4>';
        $name = '_' . $name;
        if (property_exists($this, $name)) {
            $this->$name = $value;
            return;
        }
        else {
            trigger_error("Undefined property in __set(): {$name}");
        }
    }

}

$myExample = new Example();
$myExample->my_property = 'my_property now has a value';
echo $myExample->my_property;
unset($myExample->my_property);
echo "Did I unset my property?: {$myExample->my_property}";

Example 2

class Example {

    protected $my_property;

    function __get($name) {
        echo '<h4>__get() was triggered!</h4>';
        if (property_exists($this, $name)) {
            return $this->$name;
        }
        else {
            trigger_error("Undefined property in __get(): $name");
            return NULL;
        }
    }

    function __set($name, $value) {
        echo '<h4>__set() was triggered!</h4>';
        if (property_exists($this, $name)) {
            $this->$name = $value;
            return;
        }
        else {
            trigger_error("Undefined property in __set(): {$name}");
        }
    }

}

$myExample = new Example();
$myExample->my_property = 'my_property now has a value';
echo $myExample->my_property;
unset($myExample->my_property);
echo "Did I unset my property?: {$myExample->my_property}";

As a side note, this is just a simplistic example that demonstrates the behavior I'm seeing in my real world project. Thanks!

Was it helpful?

Solution

The problem you have is that you have not defined an __unset() magic method.

This means that when you call unset($myExample->my_property), it is trying to directly unset a public property with the specified name.

In example 1, the real protected property has an underscore in it's name. Therefore, when you try to unset the property, PHP looks at the object, sees that there is nothing with the specified name, and effectively ignores it.

This is the same behaviour that unset() would exhibit if you tried to unset a non-existent variable or array element.

However in example 2, the protected property has the same name as you have given in the unset() call.

In this example, PHP looks at the object and sees that the property does exist but that it it is non-accessible. It therefore throws an error complaining that it can't unset the property.

You can resolve this issue by including an __unset() method alongside your __get() and __set() methods. If you're planning to use magic methods, you should ideally define all three.

Hope that helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top