Php __get and __set magic methods - why do we need

2019-06-17 19:15发布

问题:

On Zend Quick Start Guide here http://framework.zend.com/manual/en/learning.quickstart.create-model.html we can see:

class Application_Model_Guestbook
{
    protected $_comment;
    protected $_created;
    protected $_email;
    protected $_id;

    public function __set($name, $value);
    public function __get($name);


    public function setComment($text);
    public function getComment();
...

I normally create my getters and setters without any magic method. I've seen this on the quick guide, and I don't understand why may we need this.

Can anyone help me out?

Thanks a lot

回答1:

You (usually) never call __set or __get directly. $foo->bar will call $foo->__get('bar') automatically if bar is not visible and __get exists.

In the tutorial you've linked to, the getter and setter get set up to automatically call the appropriate individual get/set functions. So $foo->comment = 'bar' will indirectly call $foo->setComment('bar'). This isn't necessary... it's only a convenience.

In other cases, __get can be useful to create what looks like a read-only variable.



回答2:

If you "make your own" getters and setters, you'll be making two functions for each property: getPropname() and setPropname. By using PHP's "magic method" setter/getter, you don't write individual methods for each property. You can set/get properties as if they were public. Within the overload functions, you add the logic specific to each property. Example:

public function __set($name, $value) {
 switch ($name) {
   case 'comments':
     // do stuff to set comments
     break;
   case 'parent_user':
     // do stuff to set parent user
     break;
 }
}
public function __get($name) {
 switch ($name) {
   case 'comments':
     // do stuff to get comments
     break;
   case 'parent_user':
     // do stuff to get parent user
     break;
 }
}

Now I can access comments by using $obj->comments to either set or get.

To achieve the above functionality without using the overloads, I would have had to write 4 different methods instead. This is really more about code organization, both in terms of the actual file and in terms of creating a standardized interface for objects within a project.

I personally prefer to do as you do and write separate methods for each property that I need a complex getter/setter. To me, it is more clearly organized, and there is a clear separation between "simple" properties of an object and those properties which have greater complexity or one-to-many relationships.



回答3:

The __get and __set magic methods exist for TWO reasons:

  1. So that you don't have to spend time creating vanilla accessor methods for all of your properties.

  2. To allow you to implement property overloading.

If you have properties that need special treatment, then the magic methods are not for you. However, if your properties simply contain data and there are no dependencies then there's no reason NOT to use the magic methods.



回答4:

Further on the tutorial it gets you to modify the __get() and __set() methods so you'll see why they're there.

Having magic handling like that can allow you to do some neat stuff like even making "read only" properties and can prevent you from bloating your class with a messy load of getter/setters.



回答5:

You'll run into problems when you've used __set and want to override the setter functionality in a child class. You'll have to copy the entire code in __set and change only the specific part instead of just simply overwrite one specific setter function -> redundant code

This problem can be avoided by calling the specific setter in __set (thanks to cHao for the tip).

Example:

class Foo1 {
   protected $normal;
   protected $special;

   public function setSpecial($special) {
      if ($special == isReallySpecial()) {
         $this->special = $special;
      }
   }

   public function __set($key, $value) {
      switch ($key) {
         case 'normal':
            $this->normal = $value;
            break;
         case 'special':
            $this->setSpecial($value);
      }
   }
}

class Foo2 extends Foo1 {
   public function setSpecial($special) {
      if ($special == isNotReallySpecial()) {
         $this->special = $special;
      }
   }
}