It’s been a while since my last post. My blog pattern these days is that I write a blog post when I have an opinion that doesn’t fit in a tweet.
There’s been a debate in the PHP community about the use of public, private and protected. Apparently the Symfony project has decided that private is Evil, and should not be used. I don’t care much about Symfony as I’m not a user, but it turned to a discussion on OO theory when Stefan defended the position by claiming that you ‘should have the right to extend a class’s methods if it doesn’t support the use case you have’.
On Twitter, things got worse. Marco Tabini mentioned private has no role in open source code insinuating that it’s about who can read the code and Travis Swicegood mentioned that protected code indicates code that is in the wrong place and later that it prevents unit testing because it creates un-testable units of code.
Before I answer to those claims, let me give you an example of a class that uses public, private and protected in the way they were intended.
The Account class
class Account { public function depositFunds($amount) { $this->_updateBalance($this->currentBalance()+$amount); } protected function _updateBalance($newbalance) { if ($this->validate($newbalance)) { $this->__updateRecord($newbalance); $this->__publishAccountUpdatedEvent(); } } private function __updateRecord($newbalance) { $this->getModel()->store($newbalance); } .... }
As the designer of this class, I have the following considerations:
- Somebody that uses my class in an application will need to be able to withdraw and deposit funds. This is the only feature I offer to my class users; it is my class’s responsibility to take care of handling the withdrawal and deposits.
- Derived Account classes (which will follow the ‘is a’ scenario of inheritance and thus will probably be special types of accounts, such as savings accounts) should be able to withdraw and deposit, but can also call _updateBalance to update the balance directly. This is something I trust derived classes with.
- The actual act of updating the underlying model is my responsibility and my responsibility alone. This method is private because I do not want other classes to call this function directly. The only way to call __updateRecord is through the protected _updateBalance call. This way I ensure that there’s always validation and that notifications get send out. This makes my application more robust because the things that need to happen, will happen. I trust derived classes to update the balance, but the trust goes only so far; the actual act of updating the underlying model is my responsibility.
This process is called ‘encapsulation’.
Looking back at the arguments
Now let’s look again at the arguments.
- Stefan says that he needs to be able to override things if they don’t support his use case. The above class is designed to support a variety of use cases, through the interface it offers to users and inheritors. Generally a use case is defined by the public methods a class offers, and that should be sufficient. If not, the class may have been designed poorly. Inheritors can change the use case slightly, but only to the extent that they do not endanger the overall robustness of the application.
- Marco says that private has no use in open source projects. The above example could be part of an open source project. The decision to make a method private had nothing to do with the fact that others could read the code, nor did it have anything to do with the fact that the code cannot be changed. If this is an open source package and you want to change it because in your world you do want unvalidated account updates, you can simply download the code, change the code and be happy.
- Travis claims that ‘protected’ signifies code that is in the wrong place. On the contrary, in my example I’ve used protected and private deliberately to design class responsibility.
- Travis also says that it is a problem for unit testing because it creates an untestable unit. I would argue that in this case, the ‘unit’ is the account and its public interface. A unit test should use only the public interface which is sufficient to verify whether or not the class is doing the right things, and whether it’s doing those things right. If the class contains so much code that you need unit tests that test the smaller internal functions of the class, you may be looking at a class that has too much responsibilities and that should be split up.
The arguments seem to be mostly about what you can derive but the number 1 use case of access specifiers is defining what you can call. In my opinion, preventing people from using private to make deriving easier at the expense of no longer being able to specify who can call what and define class responsibilities, is wrong. In most cases where this is a problem, the actual problem is poor class design.
There are of course numerous situations where the designer of a class has made the wrong decision and made a method private that should’ve been protected. In such scenario’s, changing it and contributing a patch seems the right thing to do. However removing the use of private from a project entirely because some can’t handle the difference, seems very wrong.
Other languages
PHP is not the first language to support private, public and protected. Other languages have seen similar debates, with more or less the same underlying principles. Ruby has built in ways to circumvent private/protected/public so functions can be unit tested. C++ is the most advanced when it comes to OO design. It supports ‘private inheritance’ and ‘protected inheritance’. See here for examples. Also it supports a ‘friend’ feature that allows a class to define ‘friends’ that can access their private members. If classes befriend their unit tests, then unit tests can access private functions.
PHP doesn’t have a ‘friends’ feature (I nearly said ‘PHP doesn’t have friends’ which does sound funnier but is not what I meant
) nor does it have ways to circumvent the access specifiers for unit tests. That doesn’t make the concept of private and protected any less useful though.



A lot of people are already convinced that
A while ago, I was invited to 

