Shawn Stratton Systems Architect

16May/11Off

Accesors and Religion

Today I'm going to become flame bait, I'm going to talk about something that's highly controversial, not because most view points on it are wrong but because everyones differ and they're all wrong besides mine.  Well there went my attempt at being sarcastic, but seriously, this is one of those discussions that will always be highly controversial.

The Problem

Objects have properties which sometimes need special logic on how they are retrieved and set.  There are several solutions to this and everybody has a different view point about which is correct, none are pretty and all have drawbacks which range from writing extra code, creating something that has poor extensibility, or has issues with consistency.  These don't even breach the issues with IDE code completion and analysis.  Lets look at some of these solutions.

The Solutions

Direct Access to properties

So when you first start getting into objects the very first thing that most people do is create a whole bunch of public properties that are accessed like this

class stuff{
  1.   public $whatever;
  2. }
  3. $obj->whatever = 'whatever';

This works well, you can set things outside of the object and then have the object act on it, the problem comes in when you need to modify the way the data is handled when it's set or retrieved without hanging client code.  That's generally when people start writing accessor methods and changing client code which leads to our second solution.

Mixture of Accessors and Direct Property Accessors

Once you start seeing needs of modifying data as it's being assigned you'll start seeing code like this:

class whatever2{
  1.   public $whatever;
  2.   public $stuff;
  3.   public function setWhatever($value);
  4.   public function getWhatever();
  5. }
  6. $obj->stuff = 'stuff';
  7. $obj->setWhatever('whatever');

Usually this only lasts a little while, usually someone will completely forget about the accesor methods and work with the property directly and cause some great inconsistencies, then as an act of paranoia someone will mark the property as protected or private.  This is still a problem as it leads to great inconsistencies, then the next step comes up.

Accessors for everything

This is usually the level most hit and stay at, they start writing accessors for every property that's supposed to be publicly accessible, your code here will start to look like this:

class whatever{
  1.   protected $stuff;
  2.   protected $whatever;
  3.   public function getStuff();
  4.   public function setStuff($value);
  5.   public function getWhatever();
  6.   public function setWhatever($value);
  7. } 
  8. $obj->setStuff('stuff');
  9. $obj->setWhatever('whatever');

At this level consistency is no longer a problem and you can easily extend the functionality of your getters/setters which is awesome, some even figure out to return $this in the setters so they can do chaining, at which point you client code can look like this

  1. $obj->setStuff('stuff')->setWhatever('whatever'):

There are downsides with this though, you'll have to write two methods that have to be compiled and referenced for every property you have, this makes the code very verbose and very long.  Some people have solved this issue with __call which is called every time you call a function that doesn't exist.  This usually looks like:

class whatever{
  1.   protected $stuff;
  2.   protected $whatever;
  3.   public function __call($name, $params)
  4. }

With some logic you can easily have the function  check for get/set at the beginning of the method name; it works but it's slow and it means that IDE's won't detect the setters/getters  and magic is generally not the best thing.  The even worse case of this is people use __get and __set to overwrite everything, but it does work on every protected/public property and it's an easy way to centralize getters and setters.  This leads us to our last idea, which I personally think is the best way to handle the situation.

My Solution

Obviously we want the ability to overwrite the way things are set, we also need consistency so that we're backwards compatible and the developers don't get confused by what's set via setter and what's set via direct property access, ultimately we don't want to write a bunch of functions just to set and get the values of our properties, it's kinda silly actually, thinking of all those function calls and completely discarding PHP's Property Setting also seems a little foolish.  Anyway, ultimately I came up with the following solution and it works really well for me and the people I work with.

public function __set ($key, $value)
  1. {
  2.   //check if a accessor function exists
  3.   $accessor = 'set'.ucfirst($key);
  4.   if (method_exists($this, $accessor))
  5.   {
  6.     return $this->$accessor($value);
  7.   }
  8. }
  9. public function __get ($key)
  10. {
  11.   $accessor = 'get'.ucfirst($key);
  12.   if (method_exists($this, $accessor))
  13.   {
  14.     return $this->$accessor();
  15.   }
  16. }

With this code in our Model Abstract we are able to call everything via the default method of setting properties i.e.

  1. $object->property = $value;

Most properties would then be marked public, with properties meant to be accessed in this way being set to protected and having set/get functions written for them (that are then marked protected to keep the api clean and to keep interaction sane.)  There is one obvious glaring downside to this though, this does not work with IDE auto-completion, and boy do I wish it did, but out of all things to sacrificed that's pretty minimal.  Also I should mention that the above code does not do any error setting which has been omitted in my example.

Conclusion

There are many ways you can skin this metaphorical cat, however, they each have trade-offs.  Ultimately I went with the methodology that made the most sense to me and that passed our architectural standards (consistency) but you may have a different use case and that's fine.

Filed under: PHP Comments Off
  • lucifurious

    If you have a decent IDE, you can add @propertyphpdoc tags to your class comment to get your autocompletion.

    Also, you really should call the parent::__set. ;)

  • Not The IMF Guy

    Optimizing for writing code (instead of reading code) is almost always a mistake in my opinion.

    I guess you have no use for interfaces?

    • Anonymous

      Not at this level, but to each his/her own. My biggest concern was backwards compatibility actually and I hate writing all those dummy functions.

      • Not The IMF Guy

        I can understand the backward compatibility reason. Yes, to each his own.

  • Jblotus

    one downside you did not mention is that __get and __set are slower than using defined getter and setter functions. if you have a large number of accessors in a class, it is surely a sign that your design needs to be improved through refactoring.

    • Anonymous

      I didn’t mention benchmarks for a reason, my motivation in this case has been to make consistency key, followed by flexibility. Performance wise there are hundreds of ways to squeeze out extra milliseconds here or there but micro optimization isn’t something that interests me.

  • Pingback: Shawn Stratton writes on Accesors and Religion | PHPtrends.net

  • Pingback: Shawn Stratton’s Blog: Accesors and Religion

  • שי בן משה

    Very nice solution!
    Love it!
    Thank you for the article.

    By the way there is an error in the third code example.

  • http://www.anthonyw.net Anthony

    I still find having getter and setter methods cleaner than using __set and __get functions. Just because a class has a lot of access functions is not a clear indicator of refactoring. Sometimes classes do need to be large, sometimes they do need to handle a lot.

    • Jblotus

      assuming you are in control of the class, what is stopping you from refactoring a large class into smaller chunks? large classes tend to be a huge warning sign.

  • Pingback: Shawn Stratton’s Blog: Accesors and Religion | Scripting4You Blog

  • Pingback: Software Development | Pelshoff.com