It's all about encapsulation.
Say you use $person->age = x
. For now, it'll work fine, and let's say you have that line in 100 places. Later on it turns out that you want to restrict age
to, say, numbers greater than 0. With direct access to the member variable, there's no way to easily enforce that restriction.
But let's say you'd originally written $person->set_age(x)
in those 100 places. Now, you can change your setter from
private $_age;
public void age($new_age) {
$this->_age = $new_age;
}
to
private $_age;
public void age($new_age) {
if ($new_age > 0) {
$this->_age = $new_age;
}
}
You don't have to touch a single consumer of the set_age method; the changes "just work". That's the real beauty of OOP: you can hide a lot of implementation details behind a single method call.
The encapsulation can help elsewhere, too: say you wanted a subclass of Person that logged every age change. With the setter method, it's as easy as an override; with direct variable access, it would be a mess.