I was having a discussion on IRC about use statements and whether they improved code readability or not.
Consider this hypothetical code:
$cache = new \User\Service\Cache(); $mapper = new \User\Mapper\User($cache) $form = new \User\Form\Registration($mapper); $form->process($request->getPost());
use User\Service\Cache; use User\Mapper\User; use User\Form\Registration; // other code $cache = new Cache(); $db = new User($cache) $form = new Registration($mapper); $form->process($request->getPost());
The first snippet is completely unambiguous at the expense of verbosity. Those longer class names make it a little hard to quickly parse what it going on. The second is clearly less cluttered, but is at the expense of ambiguity. Exactly what class is User? I would have to go to the top of the file to find out. Should I use aliases? If so, how should I name them?
This is even more interesting in the context of pull requests where the use statement is already in place in the file. As a result the diff you are reviewing doesn’t have the use statement in it, so you have to go a different view to check that the class in use is actually the correct one. If fully qualified class names are used, then the PR’s diff is self-contained and easier to review.
As with a lot of things in programming, there are pros and cons, so I reached out to the people who follow me on Twitter and asked them:
Do people like “use” statements or is the separation of 100s of lines of code between the real class name and it’s actual use a nuisance?
— Rob Allen (@akrabat) March 15, 2014
There were a number of interesting responses, including:
@akrabat I think use statements just abstract where the class is coming from. Some people find that useful. Helps keep lines under 80 chars
— Herman Radtke (@hermanradtke) March 15, 2014
@akrabat I think it's helpful seeing all of the packages used by a class without having to look through the full code.
— Ben Johnson (@ben_johnson) March 15, 2014
@akrabat One reason I like them is that I can glance at a file and know dependencies immediately.
— weierophinney (@mwop) March 16, 2014
There seemed to be consensus around the use of use statements. The main reasons appeared to be the ability to see the class dependencies at the top of the file and improved code readability (less clutter).
Some people also pointed out that you can introduce clarity when importing:
@akrabat I do appreciate what you are saying about the indirection use statements introduce. Aliasing unclear class names can help.
— Richard Miller (@mr_r_miller) March 15, 2014
@akrabat Ambiguity can be solved using aliases. For example: use My\Mapper\User; vs use My\Mapper\User as UserMapper;
— Nikola Poša (@nikolaposa) March 16, 2014
@akrabat I always import the namespace below the class. So my hints are somespace\user
— Brandon Savage (@brandonsavage) March 15, 2014
If you consistently name your aliases, then the code is shorter and also just as clear. If we take Brandon’s approach, then the example above becomes:
use User\Service; use User\Mapper; use User\Form; // other code $cache = new Service\Cache(); $db = new Mapper\User($cache) $form = new Form\Registration($mapper); $form->process($request->getPost());
Now the “aliases” are codified by the PHP namespace name and so you aren’t at the mercy of the developer who names the alias. However, the list of use statements is no longer a list of dependent classes, it’s a list of dependent namespaces.
Having thought about all the responses I received and having slept on it, I think that it’s preferable to be able to organise your code and name your classes such that when importing we minimise ambiguity. If we reorganised, we could come up with something like this:
use User\UserCache; use User\UserMapper; use User\RegistrationForm; // other code $cache = new UserCache(); $db = new UserMapper($cache) $form = new RegistrationForm($mapper); $form->process($request->getPost());
We have now flattened our class hierarchy which has resulted in clearer class names. Of course, this is not always possible and in those cases, I think that consistent naming of aliases is the way to go.
Have I missed something obvious? How do you import classes?