Published: 2009
For many years, it has been an accepted de-facto standard in the PHP community to declare class properties as protected by default. The argument was somewhere along the lines of, "If you ever need to extend this class, the properties are accessible". I played along for a while, but my experiences implementing the Zend framework really showed me how awful this practice can be.
Since the developer designs the class with the idea in mind that it it can be arbitrarily extended, little attention is given to the API. What ensues is often implementations with very strong coupling.
To illustrate that point, lets take a look at an excerpt of the
Zend_Form_Element
class:
class Zend_Form_Element
{
protected $_decorators = array();
public function __construct($spec, $options = null)
{
$this->init();
$this->loadDefaultDecorators();
}
public function init()
{
}
public function loadDefaultDecorators()
{
if ($this->loadDefaultDecoratorsIsDisabled())
{
return $this;
}
$decorators = $this->getDecorators();
if (empty($decorators))
{
$this->addDecorator('ViewHelper')
->addDecorator('Errors')
->addDecorator('Description', array('tag' => 'p', 'class' => 'description'))
->addDecorator('HtmlTag', array('tag' => 'dd', 'id' => $this->getName() . '-element'))
->addDecorator('Label', array('tag' => 'dt'));
}
return $this;
}
}
Okay, so there are quite a few improvements which could be made to this code,
but thing I want to point out is that the $_decorators
property is declared
protected. When the object is constructed, that property is populated by
default. The author was kind enough to to give us an option to disable to
default decorators, but it was clearly an afterthought. Why do I say that?
Because the loadDefaultDecorators()
method does quite a few things! First it
checks to see if the defaults are disabled, then it builds a hierarchy of
expected objects before the HTML tags are even defined. So, if an implementor
merely wants to change the HTML tags with which the element is wrapped, all of
this code needs to be copied and pasted into an extension, just so you can
change the argument arrays. What makes this situation infinitely worse, is the
fact that this is the absolute base class. All of the form elements extend
this class. So if you want to retain the functionality of each of those form
elements, you need to extend ALL of them just to over-load the abstract
method.
If the abstract implementation needs to have features added to it later, it's very difficult to know how others have made use of the internal structure of the class. When you have a private property, you can check the class for references to it and identify all of the accessors very quickly. If you need to change the implementation of that property, you don't have to worry about affecting implementations or extensions of the class.
The biggest problem with protected properties is the fact that you cannot guarantee the integrity of the property.