PHP __set and __get Magic methods

Object oriented programming principles encourage encapsulation, i.e. containing all the functionality inside a class. Usually this means member variables should be private or protected, one of the common ways to do this is with various set and get methods. In the case of a user class it may have setName, getName, setAge, getAge and so on. This can be time consuming to set up, change and maintain. The magic methods __set and __get provide a quick way to implement generic set and get methods.

  1. <?php
  2.  
  3. class Foo{
  4.    
  5.     private $name;
  6.     private $age;
  7.    
  8.     public function setName($name){
  9.         $this->name = $name;
  10.     }
  11.    
  12.     public function getName(){
  13.         return $this->name;
  14.     }
  15.  
  16.     public function setAge($age){
  17.         $this->age = $age;
  18.     }
  19.    
  20.     public function getAge(){
  21.         return $this->age;
  22.     }
  23.    
  24. }
  25.  
  26. $foo = new Foo();
  27. $foo->setName("Dougal");
  28. $foo->setAge(10);
  29.  
  30. echo $foo->getName(). " is " . $foo->getAge(). " years old\n";
  31.  
  32. class Bar{
  33.    
  34.     private $name;
  35.     private $age;
  36.    
  37.     public function __set($var, $val){
  38.         $this->$var = $val;
  39.     }
  40.    
  41.     public function __get($var){
  42.         return $this->$var;
  43.     }
  44.    
  45. }
  46. $bar = new Bar();
  47. $bar->name = "Dougal";
  48. $bar->age = 10;
  49.  
  50. echo $bar->name . " is " . $bar->age. " years old";

The script outputs the same thing twice, just showing different methods;
“Dougal is 10 years old
Dougal is 10 years old”

In the above example class Foo uses set and get methods while class Bar uses the magic methods. To summarise __set and __get, basically if you are trying to access a property of an object (and it has a __get method) the name of the property you are requesting is passed into the get method and it can handle it how it wants. __set works the same but it is only called when you attempt to assign a value to a member variable. The only difference being __set takes two paramemters, the variable its setting and the value.
You might wonder what the point in __set and __get is, well one good example is when a class has dynamic properties.

  1. <?php
  2.  
  3. class DynamicFoo{
  4.  
  5.     private $vars = array();
  6.  
  7.     public function __construct(){
  8.    
  9.     }
  10.  
  11.     public function __set($var, $val){
  12.         $this->vars[$var] = $val;
  13.     }
  14.  
  15.     public function __get($var){
  16.         if(isset($this->vars[$var])){
  17.             return $this->vars[$var];
  18.         } else {
  19.             throw new Exception("Property ‘$var’ does not exist");
  20.         }
  21.     }
  22.  
  23. }
  24.  
  25. $dynamicFoo = new DynamicFoo();
  26.  
  27. $dynamicFoo->hello = “world”; // sets the properly
  28. echo $dynamicFoo->hello; // displays “world”
  29. echo $dynamicFoo->bar; // throws an exception
  30.  
  31. ?>

You can now also do some slightly more interesting things too… take this example…

  1. <?php
  2. class DynamicUser{
  3.    
  4.     private $firstname = "";
  5.     private $surname = "";
  6.  
  7.     public function __set($var, $val){
  8.         $this->$var = $val;
  9.     }
  10.    
  11.     public function __get($var){
  12.    
  13.         if(isset($this->$var)){
  14.  
  15.             return $this-$var;
  16.  
  17.         } elseif(method_exists($this, $var)){
  18.        
  19.             return $this->$var();
  20.        
  21.         } else {
  22.  
  23.             throw new Exception("Property ‘$var’ does not exist");
  24.         }
  25.     }
  26.  
  27.    
  28.     public function fullname(){
  29.         return $this->firstname . " " . $this->surname;
  30.     }
  31.  
  32. }
  33.  
  34. $x = new DynamicUser();
  35.  
  36. $x->firstname = "dougal";
  37. $x->surname = "matthews";
  38. echo $x->fullname;

I wouldn’t really recommend this way, its more an example of what can be done rather than anything else. Basically the above method “guesses” that if the property doesn’t exist you meant to call a method by the same name and returns the result of that method instead.

The nice thing about this is it can be completely transparent to the user.

Next I’ll be looking at a __call, a magic method for when you call functions!

18 Responses to “PHP __set and __get Magic methods”

  1. Peter Raitt Says:

    So if you implement __set & __get inside a class, then does that mean all your private variables are now exposed? Can you limit it to only operating on certain variables?

    Ta.

  2. Dougal Says:

    Yes, this would mean that they are all then made public. You can of course manage it inside the __get/__set method. Various ways to do this, a switch statement would be one.

    However, the real puprose for me with these methods is to allow for more dynamic coding. Thus aiming towards are more dynamic OO style rather than say Java style oo.

  3. Xanthir, FCD Says:

    To clarify what Dougal said, *if you implement __set() and __get() as done in this blog post*, then all your variables essentially become public.

    Obviously, then, implementing __set() and __get() as done in this blog post isn’t very useful (just declare all your vars public instead). It was done simply because it was very simple and illustrated the way the functions worked well.

    In *real* code, using __set and __get can be very interesting. That very last example that Dougal provided - where __get() first checks if the var exists, and if it doesn’t calls a function named after the var, is in fact very useful. It’s exactly the pattern used in languages like Ruby and Lisp (using CLOS), and even C#. There’s an excellent script here to implement it automatically in PHP: http://railsforphp.com/2007/12/21/accessing-attributes-in-php-objects/

    That linked script sets up your __get and __set functions (you have to inherit from the provided class) so that you can register a set of variable names that should be “magic”. Whenever someone tries to access a “magic” variable (say, as $myObj->var), your __get function will first check if there is a function named getVar(). If so, it calls it. If not, it then checks if there is a *protected* variable named $_var. If so, it returns it. If not, it then checks if you’ve set up a function named _get() (notice the single _). If so, it delegates to it. If none of this works, it throws an error.

    This lets you do really cool stuff, and I use it all the time. Frex, I put together a simple class that, among other things, stores a list of links to things. I originally exposed $link as a simple public variable, but realized later that *sometimes* I’d want to return the stored $link value, but other times I’d want to return another link entirely. I was faced with potentially refactoring all of my code that asked for $obj->link into $obj->getLink(). Instead, I popped this in, changed my public $link to a protected $_link, added a getLink() method, and it all worked transparently. My code still just asked for $obj->link, but it was *really* calling $obj->getLink() and everything worked wonderfully.

  4. Dougal Says:

    Thanks for taking the time to expand on that for me. Some very interesting points.

    I’ll be checking out that link!

    I do feel slightly uneasy about the magic methods sometimes. For example, having so much functionality made ‘invisible’ could lead to some very hard debugging!

  5. Umair Says:

    Instead of creating a new property every time, can’t you just store them in an array.

    add property:
    private $vars = array();

    public function __set($key, $val)
    {
    $this->vars[$key] = $val;
    }

    public function __get($key)
    {
    return $this[$key];
    }

  6. Dougal Says:

    Indeed, you can indeed. However, either way is valid. So I guess thats just down to preference

  7. Roman Says:

    Umair said in post #5:

    add property:
    …..

    What is that? I think I’ve seen this in other people’s code but haven’t been able to find out the meaning of it. Looks like a label of some sort, but PHP doesn’t have it, does it? And I think I’ve seen it in Javascript code.

  8. Dougal Says:

    Roman,

    I believe Umair meant add the property $vars to the class. A property is just a class variable.

    Perhaps you are talking about something else? If you can show me the JavaScript code I may be able to understand what you mean.

  9. Roman Says:

    You are probably right, Dougal. But here’s a little of a Javascript code from Jquery library:

    jQuery.fn = jQuery.prototype = {
    init: function( selector, context ) {
    // Make sure that a selection was provided
    selector = selector || document;

    What’s the init: for? This must be a part of JS syntax I haven’t gotten to yet.

  10. Roman Says:

    Oh, it is initialization of an associative array, isn’t it?

  11. Dougal Says:

    init is actually something jQuery has added to JavaScript, its their own implementation of a constructor.

    The above would look something like this in PHP…

    class jQuery{
    public function __constructor(selector, context ){
    // Make sure that a selection was provided
    selector = selector || document;
    }
    }

  12. Dougal Says:

    Sorry, indentations don’t work in my comments but you can see it more easily here http://pbin.eu/4/ ;)

  13. vijay nair Says:

    class div{

    $nom;
    $denom;

    function do_it()

    {

    echo $nom/$denom;

    }

    }

    how would you use a set method to validate $denom from entering “0″

  14. Dougal Says:

    I don’t think I would, I would probably check it in the do_it function.

    However, if you really wanted to… Something like this could work http://pbin.eu/5/ but its not really that nice…

  15. Fozzy Says:

    “The script outputs the same thing twice,[...]”

    Of course it does! It’s the same echo statement. =p

    If you look at your first bit of code, the second “echo” statement is using “$foo” again, instead of “$bar”. Looks like it was a copy/paste but forgot to update the proper variable. It might be a little confusing for the less experienced. =)

    Thanks for the post. I need the quick refresh!

  16. Dougal Says:

    Fozzy, nice catch! That was a bit silly of me…

  17. Brian Says:

    Why would declaring a private member and defining __get and __set methods within a class effectively make the private member public?

  18. Padma Says:

    PHP code to parse XML using PHP Magic Methods

    I do need dynimically access object parameters from xml parser.But donot have any ideas onhow to dynamically access object parameters.How to implement magic code on it.
    Here is my static code:

    foreach ($x->data->DIPENDRA20090320[0]->ENTRY as $valuei) {
    echo “ID: “.$valuei->_text->ID;
    echo “Name: “.$valuei->_text->NAME;
    echo “Address: “$car->_text->ADDRESS;
    }

    Let me know whenever you will get this message.I would really appreciate
    your help.

    Regards,
    Padma

Leave a Reply