magic __get and __set style?

For a long time I’ve held that __get and __set in PHP were not all that hot  - mostly because it’s solely error handling.  There’s no way to deal invoke __get or __set behaviour for properties that are defined on a class.  That’s sort of a beef for another post (I’d started an RFC some time ago on trying to extend that behaviour to defined properties as well as undefined, but didn’t finish it, life got in the way, and various other reasons – again, perhaps for another day).

For those who insist on using __get/__set, I *typically* see this sort of style code:

class foo
{
 protected $_holder = array();
  public function __set($name,$value) {
   if($name=='foo') {
    $this->_holder['foo'] = $value;
   }
   if($name=='bar') {
    $this->_holder['bar'] = $value;
   }
// etc
  }
}

The effect is to cram a bunch of unrelated code in to the __get/__set overloading methods.

Should I need to use __get/__set again, my new approach will be

<?php
class foo
{
 protected $__foo;
 protected $__bar;
 
  public function __set($name,$value) {
   if(method_exists($this,"set$name"))
   {
     $this->{"set$name"}($value);
   }
  }
  public function __get($name) {·
   if(method_exists($this,"get$name"))
   {
     return $this->{"get$name"}();
   }
  }
  protected function setFoo($value)
  {
    $this->__foo = $value;
  }
  protected function setBar($value)
  {
    $this->__bar = $value;
  }
  protected function getFoo()
  {
    return $this->__foo;
  }
  protected function getBar()
  {
    return $this->__bar;
  }
·
  }
$f = new foo();
$f->foo = "new";
echo $f->foo;

This feels cleaner – perhaps others of you already take this approach? It’s just not commonly reflected in PHP tutorial sites that I come across, and it’s been bugging me for a while. I also see “mysql_connect() or die()” in books published in 2010 though, which indicates the majority of stuff published on PHP is still behind the times.

What I’d like to see (and what my RFC would have been about) is PHP to look for the existing of internal getXXX and setXXX methods on property accesses all the time, and not just fall back to __get/__set on error conditions. Being able to call $foo->name, and having PHP call getName() *if the method exists* would make for easy to read code that’s also very extendable.

Perhaps I’ve written about this before – I can’t remember. Certainly, some of our PHP user group has heard me go on about this before. Now it’s there for the ages. :)

How do the rest of you deal with wanting to have overridable property access? Do you just have manual get/set() methods for every property access? Or some other approach?


I'm currently working on a book for web freelancers, covering everything you need to know to get started or just get better. Want to stay updated? Sign up for my mailing list to get updates when the book is ready to be released!

Web Developer Freelancing Handbook

Share and Enjoy:
  • del.icio.us
  • DZone
  • Facebook
  • Reddit
  • StumbleUpon
  • Digg
  • Simpy
  • Technorati

{ 14 comments to read ... please submit one more! }

  1. Frankly speaking I don’t like both approaches you described, anyway you have to write whole bunch of unrelated code. In my project I’ve written abstract base class for models and described __call method like this (’cause it’s very boring to write stupid get/set methods):
    public function __call($name, $args)
    {
    $accessor = substr($name, 0, 3);
    $property = substr($name, 3);
    switch($accessor) {
    case ‘get’:
    if(‘mapper’ == strtolower($property) || !$property = $this->validateAttribute($property)) {
    throw new BadMethodCallException(‘Getting property error: Invalid property ‘ . $name . ‘!’);
    }
    return $this->$property;
    break;
    case ‘set’:
    if(‘mapper’ == strtolower($property) || !$property = $this->validateAttribute($property)) {
    throw new BadMethodCallException(‘Setting property error: Invalid property ‘ . $name . ‘!’);
    }
    $this->$property = $args[0];
    return $this;
    break;
    default:
    throw new BadMethodCallException(“Calling to unknown method or property ”
    . $name);
    }
    }
    Here is the validateAttribute method:
    protected function validateAttribute($name)
    {
    $name = $this->_lcfirst($name);

    if (in_array($name,
    array_keys(get_class_vars(get_class($this))))) {
    return $name;
    }
    return false;
    }
    Then my real model class extends the base one and i specify only properties:
    // class definitiion here
    // class body
    protected $id;
    protected $name;
    // and so on
    No set/get or magic methods in real model class. The result is you can invoke methods like this:
    $object->setId($id);$object->getName();
    Besides I realized ArrayAccess interface in abstract class to simplify object’s use in real world projects. That’s my scheme. Hope it will help you:)

  2. This all looks like unnecessary overhead to me – much like using OOP for the sake of using OOP – instead of trying to come up with a short, precise, well-readable solution people try to enforce patterns upon everything.

  3. @miholeus

    I’d agree that doing this in a base class would be better – I probably would if I took this approach. I’m used to having this behaviour in Groovy, and would probably seek to emulate it in the next PHP project (probably depending on many other factors as well).

    I’m probably in a minority in the PHP world, but I just hate writing set/get prefixes in front of property accesses. Given the context of assignment or not, the system *knows* if I’m GETting or SETting. Similarly, the parser should be able to tell that in an abstract class, a method with no body is, in fact, abstract, without me having to identify it with the word ‘abstract’, but such is life. :/

    Thanks for sharing your approach!

  4. I am torn 50/50 on this topic. If you are heading towards the approach of using __get/__set you might as well just have public properties and literally set/get the values. The only time a get/set method is needed is if it does some kind of manipulation before setting/getting. Example, inflecting a last name when setting.

  5. Although you only published example code, I must remark that naming variables and function names with two underscores as a prefix is discouraged by PHP because such names might be used for new magic methods like __get and __set.

  6. Every approach with the current PHP possibilities is as bloated as the other one, I think. Using a variation of your second approach is probably the most clean one, but it’s still tricking someone who watches client code like:

    $bus->driversName = ‘Niles’

    that there’s not a method call involved. Compare it to

    $bus->setDriversName(‘Niles’)->driveOff()

    makes for example chainability much more visible, besides, the method call can not be mistaken. Nothing weird going on either with method overloading.

    That said, the syntax takes too long to write (getters/setters in all classes) and a base class is out of the question (when is a class a “base class” and when does it really need the private & protected members to be just that – private & protected. (Let’s face it, getters & setters are for public members with a twist).

    The only valid future I could imagine for a shortcut, would be this: http://wiki.php.net/rfc/propertygetsetsyntax

  7. This has been discussed in internals for the past couple of weeks. There’s also an RFC:
    http://wiki.php.net/rfc/propertygetsetsyntax

  8. @david – thanks – I haven’t followed internals in a while. Looks interesting – I’d only started my RFC – I think this one has more detail.

    I think I’d still favor a groovy approach, which simply has the engine look for a corresponding getXX or setXX method when something is accessed. If something exists, it would be called – this optimization could be made at runtime, and it would make things much easier to integrate to existing code with a minimum of fuss. At least, that’s my view.

    I’ll keep an eye on this – would be great to have in PHP6.

    Thanks.

  9. @Miles Johnson, I have to respectfully disagree. The primary purpose of creating accessors (setters) and mutators (getters) is encapsulation. It’s about hiding how the properties are implemented within the class. If you expose them publicly and encourage people to use the $object->property = value approach to changing them, the properties become part of the interface of the object and cannot be simply and easily changed by a future revision.

    One of the objectives of OOP is to hide the implementation of the object so that it can be treated as a blackbox. Exposing properties publicly violates this intent and commits you to maintaining the property even if you rewrite the object.

    In response to the general argument, what’s so terrible about simply writing setProperty and getProperty methods for the individual properties? Yes, it’s repetitive, but it can be quickly setup with copy and paste.

  10. @fergus

    Not sure about anyone else, but for me, it’s simply really a huge pain, and *should* be unnecessary. One of PHP’s strengths has traditionally been it’s lack of boilerplate code, relative to heavyweights like Java. Requiring manual get/set code all over a codebase just looks wrong (again, just imo). Having 50 getter/setter methods at the top of a class which do almost nothing, but having them just in case your design changes later (to avoid requiring a lot of refactoring down the road) – it’s just wholly wasteful.

  11. @all

    What seems to be missing from discussions like this is the notion of ever having to change *any* existing get/set behaviour in existing code.

    Currently, the only way to do that right now is to have explicit get/set stuff all over your code, and have 99-100% of them doing nothing but adding overhead (visual and runtime) to the code on the off-chance you need to change the behaviour at some point.

    Having some degree of dynamicness allows for easier to read code but the ability to change behaviour in the future without having to rewrite potential large amounts of code. I’m pretty convinced what I’d put above really isn’t a *great* way (and isn’t even complete, really) but would serve the purpose of allow flexibility should the need to change default behaviour, and to keep the code changes to a minimum.

  12. You should throw an Exception in case the attribute doesn’t exist.
    Its especially bad with the setter, as it will just succeed but not set any value, but the getter is also prone to error.

  13. I often get around creating getters and setters for all variables by:

    public function __get($name) {
    $getter = ‘get_’.$name;
    if (method_exists($this, $getter)) {
    return $this->$getter();
    }
    elseif (isset($this->data[$name])) {
    return $this->data[$name];
    }
    else {
    trigger_error(“…”, E_USER_WARNING);
    }
    }

  14. My one cent:
    1. I’m with Sven on the two leading underscores in the attribute/property names.

    2. I’m with Oliver on the exceptions, probably best to do something similar for item 3, too:
    public function __get($propertyName)
    {
    $getter = ‘get’ . $propertyName;
    if (!method_exists($this, $getter)) {
    throw new Exception(‘Property by name ‘ . $propertyName . ‘ not found as part of this object.’);
    }
    return $this->{$getter}();
    }

    3. Pros and cons to the following, of course, but you can sometimes get away with something like this:

    protected $_attributeList = array(‘foo’, ‘bar’);

    // silently ignoring non-existent attributes may not be the best idea…
    public function setFromArray(array $data)
    {
    foreach ($_attributeList) as $property) {
    if (isset($data[$property])) {
    $this->{‘_’ . $property} = $data[$property];
    }
    }
    }

{ 0 Pingbacks/Trackbacks }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">



0.45682692527771