Use statements
I was having a discussion on IRC about use statements and whether they improved code readability or not.
The choices
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());
vs
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.
Getting advice
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.
My thoughts
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?
For me, and many other's I'd say, it's pretty common to suffix any classname except the entity itself with the parent namespace. Thus I usually end up with the following classes:
Module\Entity\User;
Module\Form\UserForm;
Module\Mapper\UserMapper;
And so on… I guess you get the idea. The point behind that is not only the ability to use all those classes without aliasing them, but also because else you have several files open in your IDE, all labeled "User.php", which is simply just confusing.
That is exactly the way I use namespaces. +1
I had a lot of trouble getting comfortable with namespaces and imports. One rule I kind of learned during my adventures was:
A class name should always make sense without the namespace.
So a class name like
vendor\Validator\String
are confusing, when you import them (and have no alias) and then do$validator = new String();
. So in a lot of cases, I try to add the last namespace to the class name:vendor\Validator\StringValidator
. Now the name makes sense on its own and you don't need to alias it.i think the root problem is sub-namespaces should be used strictly sparingly. having 1 namespace per class is an antipattern…(i see a lot of code with too much of that) and adds 0 to the value the class brings in.
I tend to alias the implementation as the name of the interface they implement (as I prefer at that level to deal with the abstraction):
// UserRepository.php
<?php
interface UserRepository
{
/* contract here */
}
// DbUserRepository.php
<?php
namespace Foo\Bar
class DbUserRepository implements UserRepository
{
/* implementation here */
}
// SomeOtherClass.php
<?php
use \Foo\Bar\DbUserRepository as UserRepository;
class SomeOtherClass
{
/* implementation here */
}
Using same class name in a client class from different namespaces is usually the exception. The code is nice and clean with use statements. Think of Java, the source of inspiration, they don't use full package name, and when they do, it looks horrible.
I come from Java, and I like use statements for readability. I use Eclipse so I can always drill through to the actual class just like in Java. The actual use statements are collapsed and something I rarely look at.
I like the use statements at the top for the reasons the others have mentioned but there are certain cases where is use them inline.
One thing I this is important though is that you don't have too many, if a lot start to pile up then I use that as an alert that the class may be getting to complex.