There's very little reason to use __get, __set (or __call for that matter) in a way where the actual data structure is fixed (e.g. you have a fixed set of members and only access them through those methods.
The advantage of these methods lies in situations where you don't actually have a fixed structure. While these situations should usually be avoided there are some situations where this may become handy.
For instance I have a model class for a very lightweight ORM which doesn't require code generation and still features a public interface similar to more complex ActiveRecord style frameworks (i use __call in this and extract the field name from the called method, but __get/__set would work as well).
class User extends AbstractModel {
protected static $FIELD_LIST = ['id', 'username', 'password'];
}
$foo = new MyModel();
$foo->setId(123);
$foo->setUsername('Foo');
$foo->setPassword('secret');
$foo->setNonExistantField('World!'); // will throw an exception
This allows me to rapidly create a model class where at any point I can decide to write a custom setter method. e.g. if I wanted to store that password as a salted hash I could do something like this:
class User extends AbstractModel {
protected static $FIELD_LIST = ['id', 'username', 'password'];
public function setPassword($password) {
$salt = magic_salt_function();
$hash = crypt($password, '$2a$08$' . $salt);
$this->data['password'] = $hash;
}
}
The advantage being that I don't have to write getter/setter methods for every field but at any point can. Very handy in rapid prototyping.
Similar techniques can be used for example if you have some data in array from which you want to modify with object syntax. Using __get/__set allows you to avoid having to go through the array whenever you leave object context back to array context.
class Foo {
protected $data;
public function __construct(array $data) {
$this->data = $data;
}
public function __get($key) {
if(!isset($this->data[$key])) {
throw new Exception("Unknown member $key");
}
return $this->data[$key];
}
public function __set($key, $value) {
if(!isset($this->data[$key])) {
throw new Exception("Unknown member $key");
}
$this->data[$key] = $value;
}
public function getData() {
return $this->data;
}
}
$data = [
'bar' => true,
'braz' => false
];
$foo = new Foo($data);
$foo->bar = false;
$foo->braz = true;
$foo->nope = true; // will throw an exception
In the end overloading in PHP is a tool for a pretty specific task (creating dynamic interfaces). If you don't need it, you don't use it. And when you use it, you should be aware that it has its downsides. After all once you overload you're in charge of validation that normally the interpreter could do for you.