6 good practices for use in PHP

6 good practices for use

6 good practices for ‘use’ in PHP

While reviewing code recently, I realized there are no guidelines for use. Every modern code do use ‘use’ (sic), as importing classes from composer or a framework is the norm. There are now quite a few variations in syntax for importing classes or traits. Although the impact on performance is low, as use is solved out, having a clean approach to ‘use’ is important to keep the code clean and readable. Let’s review what are the six good usage of use.

The multiple use expressions

Uses configurations come in three flavors : namespaces, traits and closures.

Closure have use in its syntax. This usage is strictly formatted, with little variation allowed.

<?php
function ($arg) use ($localvariable) {}
?>

Trait use are used inside a class or another trait. It imports a trait inside the current structure. Although PHP doesn’t check for multiple import of the same trait, it doesn’t have much impact, besides looking weird. Nonexistent trait generate a Fatal error, and that’s about it.

<?php
trait t { use t1 ; }
class foo { use t ; }
?>

We’ll consider here the namespaces use. They come in three flavors, again : single, multiple or grouped. All flavors have the same configuration purpose on the code. They may also be explicitly aliased or not.

<?php

use single\import ;
use multiple\import, multiple\import2, multiple\import3 ;
use grouped\import{ classes, interfaces, traits} ;

use single\import as aliasedImport ;
use multiple\import as aliasMultipleImport, multiple\import2 as aliasMultipleImport2, multiple\import3 as aliasMultipleImport2 ;
use grouped\import{ classes as groupedClasses, interfaces as groupedInterface, traits as groupedTrait} ;

use path\to\aClass ; 
use path\to\folder as prefix;

new aClass ;
new prefix\classA ;
new prefix\classB ;

?>

Best practices with use

  • Do not import unused classes
  • Alway use alias
  • Place all use at first
  • Avoid using the same alias for different classes
  • Group use expression for easy reading
  • Limit the number of use expression

Do not import unused classes

This one is quite obvious, and well known. It applies to all importable structures. PHP doesn’t check that all use are used at least once, so there are no mention of useless used. This is mostly dead-code. It is used at compile time, and might slow compilation : this shouldn’t be measurable. Dead ‘use’ doesn’t change execution performances. It’s good to keep ‘use’ clean, to avoid confusion or extra cost of maintenance or confusion.

<?php

use path\to\A , path\to\UnusedClass ;

new A ;
?>

Always use aliases

Any call to a fully namespaced should be avoided, and at least aliased. Firstly, this shorten the code, keep it easier to read. This may also be called ‘Avoid long classes name in code’.

<?php

new path\to\A ;

?>

It also help when refactoring : all namespaces usage are now options of the script. Changing the use expression allows changing all subsequent usage of the alias. This is especially true when the namespace is used several times in the code, saving multiple search and replace in the code. Note that in the long run, it may not be so wise to keep prehistoric names in modern code.

Using alias prevent the silent dead code coming from unresolved classes in instanceof, catch() or typehint. Those clauses don’t check that the filtered class actually exists. Indeed, when refactoring, classes may easily be changed of namespace or renamed, leaving instanceof orphan, creating dead code. With a well put use, all those would migrate just like other classes.

<?php

use path\to\A ;

$a = new A() ;
echo A::someConstant ;

if ($b instanceof A) {
	doSomething() ;
}

?>

Place all use at first in the namespace

Uses are expected at the beginning of the file, or the namespace block (They actually impact the namespace, but since most convention is one file, one namespace, it may be confused).

<?php

namespace Exakat ;

use Foo\Bar ;
use Bar\Foo ;

$a = new Bar( Foo::Constante) ;

?>

In fact, use may be placed anywhere at the root level of the namespace, with significant impact on the readability and usability of the code. Although they are used at compile time, the position at which the ‘use’ is place has impact of the availability of classes before and after. We won’t provide any example, but try it in a safe environment and please, don’t burn yourself.

Avoid using same aliases for different classes

Now this one is slightly more controversial, but I’m sure you already got bites by it.

It applies to project-wide naming schemes, with the various alias name being used in various files. Imagine that the same name is given to different classes. Those classes may actually be distinct, being located in different namespaces, but once they are aliased, or reduced to their final name, they look alike. It is then easy to mistake one for the other, until the code hits the point where they are not.

file a.php

<?php
use path\to\A as B;
?>

file b.php

<?php
use path\to\B;
?>

It is recommended to use aliases names that are specific to one class, and use them consistently across the application. Easy right ?

Well, first, if a class (or an interface, or trait) got a name, why would we need another name when actually using them ? Can’t we give this ‘other’ name directly to the class, and then use it straight ? Then, what is the purpose of namespaces again ?

Secondly, there are the special case of mock classes, that are meant to replace entirely one class with mocked values or methods. Now, ‘use’ is exactly made for this, allowing to use the same code with different classes. This may be one exception.

Anyhow, it happens often that several classes ends up with the same name in a project. Those would need aliasing, and probably some review anyway.

Group use expression for easy reading

In PHP 5, simply sort the use calls so it’s easy to see a family of use, based on the same namespace prefix. In PHP 7, use the grouped expression, to make the prefix obvious.

<?php
//PHP 5
use Thelia\Form\BaseForm;
use Thelia\Form\Definition\AdminForm;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Form\ProductModificationForm;

use Thelia\Model\AccessoryQuery;
use Thelia\Model\AttributeAv;
use Thelia\Model\AttributeAvQuery;
use Thelia\Model\AttributeQuery;
use Thelia\Model\CategoryQuery;
use Thelia\Model\Content;
use Thelia\Model\ContentQuery;
use Thelia\Model\Country;
use Thelia\Model\Currency;
use Thelia\Model\CurrencyQuery;
use Thelia\Model\Feature;
use Thelia\Model\FeatureProductQuery;

?>
<?php
// PHP 7
use Thelia\Form\{BaseForm, 
                 Definition\AdminForm,
                 Exception\FormValidationException,
                 ProductModificationForm };
                 


use Thelia\Model\{AccessoryQuery,
                  AttributeAv,
                  AttributeAvQuery,
                  AttributeQuery,
                  CategoryQuery,
                  Content,
                  ContentQuery,
                  Country,
                  Currency,
                  CurrencyQuery,
                  Feature,
                  FeatureProductQuery};

?>

Limit the number of use expression below 10

When a file start with a (too) long list of use, it shows that the following code is actually coupled to many other classes. Now, based on rule ‘always use alias’, measuring coupling is now a matter of counting use commands.

Drupal, octobercms and shopware have up to 30 use in one file, thelia has up to 69 (see here). Truthfully, average is between 5 to 8 per file, with higher numbers for Test files.

Automatically check your code

Use expression only impact the current namespace, or file. This makes it quite easy for static analysis tools to review it file by file, and provides feedback on uses based on all the rules above : missing alias, dead alias or aliases that may be reused. The exakat engine has three specific analysis to deal with those validations.

In the long run, keeping use clean and not too frequently used is the most important for your code. Use static analysis to avoid the tedious task to review, count and clean all those use expressions. Thinking about it, this may very well be totally automated !

Tweet about this on TwitterShare on RedditShare on LinkedInEmail this to someone