Minimalistic approach to class getters and setters
Getters and Setters are the backbone of many PHP classes (or any programming language class), as they allow you to alter and retrieve class properties during runtime. Many well thought and powerful scripts and frameworks make use of Getters and Setters, but is there a thing as too much? Or are there better and easier alternatives? (To make it easier on me to type, and you to read, I will refer to Getters and Setters as GnS from now on.)
By now everyone should know of the Zend Framework. It's a highly customizable and robust system built with multiple components. Zend follows the pure OOP paradigm in which most, if not all classes have an abstract, an interface, and tons of GnS. For a beginner, this might look like a huge cluster of code, as well as being very large in its documentation. But for an advanced user, it could be a god send. Personally, I find Zend's use of GnS to be too much, as it can easily be trimmed down and packaged accordingly.
Before we begin, I want to outline when and how GnS should be used. GnS should be used to alter protected properties only. Why protected you ask? If a property was public, then you can just alter the property manually without the need for a median method (unless of course the method does some manipulation on the argument). If a property is private, then that property should not be altered at all during runtime, as it is data specifically generated/built by the class internally. So that leaves protected properties to act as our configurable properties.
In a typical class, when dealing with the property $_name, you would have a method getName() and setName(). Now imagine you have a class with 15+ properties; you will immediately begin to realize the scale and amount of code required to do GnS. That's where our little friends __set(), __get() and __isset() come in handy. We can easily scale down the code from 30 methods (15 for getting, 15 for setting) to 3. Take the following before and after classes:
// Using individual methods
class User {
protected $_name;
protected $_email;
public function getName() {
return $this->_name;
}
public function setName($value) {
$this->_name = $value;
}
public function getEmail() {
return $this->_email;
}
public function setEmail($value) {
$this->_email = $value;
}
}
// Using magic methods
class User {
protected $_name;
protected $_email;
public function __get($property) {
return (isset($this->{'_'. $property}) ? $this->{'_'. $property} : null);
}
public function __set($property, $value) {
if (isset($this->{'_'. $property})) {
$this->{'_'. $property} = $value;
}
}
public function __isset($property) {
return isset($this->{'_'. $property});
}
}
Does that not look a lot easier? Sure the code looks a bit "hacky", but its perfectly usable and valid code. Of course, there are a few downsides related to this approach. For example, you may want to format a string before assigning it within setName(). With the magic methods you can not do so. But that doesn't stop you from creating a setName(), along side using the magic methods. Furthermore, the get syntax is different; you simply call the property (assuming there is no $name that conflicts with $_name).
// Using individual methods
$name = $User->getName();
$User->setName('Miles');
// Using magic methods
$name = $User->name;
$User->name = 'Miles';
// Times when a set method is needed
public function setName($name) {
$this->_name = ucall($name);
}
I would like to take this a step further, as I still believe that this is too "cluttered". The next approach solves the problem of conflicting property names, as well as reducing the code required. The approach is straight forward; simply create a global $_config (or $_data, what ever suits you) property that will deal with all the getting and setting of data.
class User {
protected $_config = array(
'name' => null,
'email' => null
);
public function __get($property) {
return (isset($this->_config[$property]) ? $this->_config[$property] : null);
}
public function __set($property, $value) {
if (isset($this->_config[$property])) {
$this->_config[$property] = $value;
}
}
public function __isset($property) {
return isset($this->_config[$property]);
}
}
In the end, it really boils down to the architecture of your application, and your personal coding preferences. Each approach has its pro's and con's, but the best solution (in my mind) is combining them. You would begin by creating the global $_config property, and building the magic methods. If you ever need to customize a get or set, then you create a specific method for it.
5 Comments
for example I prefer the "one and only one array member variable" for the database connection class.
But, in the AppController in CakePHP, I prefer listing all the member variables but I do avoid the GnS as much as possible.
as for the classic approch, getName, setName(...), I think few beginners are still using it... It is for learning purposes and It is a no-headache solution.
I am not really friend with magic methods, especially that they are magic... so you do not know when PHP core team is going to change
them in future version so the magic disappear
I am also a fan of fixing private members (variables/functions) with two undersores and the protected ones with one undersocre, and finalizing things (classes and methods), and sometimes abstracting things (functions and classes) and using interfaces, also I am a fan of combing singleton pattern with the strategy pattern (as when using/creating a connection manager)...etc.
GnS should be used sparingly, in my opinion. Only in the case that the result of getting needs to modify the instance data, or setting needs to do more than simply set the value, should you use a method to perform the operation.
Otherwise there is no benefit. I understand that your example is setting on an internal $_config array, but otherwise, there is no benefit over public variables if you are providing basic public getters and setters.
Secondly, Pear packages use the same approach, but public/protected have no underscore, where as private does.
Comes down to preference though
one thing though
if u already use "private, public, protected" which already are >= PHP5
there is no need to use the _ or even __ to distinct between them.
those where needed in PHP4, but in PHP5 you cant access them anymore anyway (by accident or purpose).
so it would probably make the code both cleaner and prettier to use the default function and variable names.