Services code completion

Due to the dynamic nature of PHP and lack of type information, code completion is still a problem in PHP. The most common difficulty is about Factory/Container methods which can return different classes as a result.

Let’s say we use \Drupal::service() in order to get EntityTypeManager instance:

$entityTypeManager = \Drupal::service('entity_type.manager');

PHPStorm has no information about the return type, it will not popout a list of available methods when you type $entityTypeManager-> . This issue can be solved in different ways.

DocBlocks

The one solution to this is to manually annotate the variable and provide type information in DocBlock.

/** @var \Drupal\EntityTypeManager $entity_type_manager

$entityTypeManager = \Drupal::service('entity_type.manager');

But then we need to do this trick for every service call and lookup service definitions manually. On the other side, it is an IDE-independent solution, anyone who reads our code will understand what is returned from the method call.

Symfony Plugin

The other solution is Symfony support plugin. This plugin integrates the next things:

Detects ContainerInterface::get() result type and provides services autocompletion.

result type and provides services autocompletion. Adds references for services in YAML files.

There also other features not related to DI container, full description here.

The problem with this plugin is that Drupal::service() code completion doesn’t work because this plugin supposed to be used with Symfony and ContainerInterface::get() calls.

Another plugin is Drupal Symfony Bridge, but it was last updated on Dec 19, 2017, and has the same issue.

PHPStorm advanced metadata

It’s a special file called .phpstorm.meta.php and placed inside your project where you can define return types for method calls based on the arguments. The file can reside anywhere, but it’s recommended to put it in the project root. Example of file contents:

<?php



namespace PHPSTORM_META {



override( \Drupal::service(0),

map(['entity_type.manager' => \Drupal\Core\Entity\EntityTypeManager::class])

);

This will tell PhpStorm that in order to determine the return type of \Drupal::service call it should use a map/associative array, where the key is the method argument value and the value is the class of the returned object.

Unfortunately Drupal::service(1) syntax is not supported, which means we can only define metadata for Factory style methods where the result type depends only on the first argument. Thus we can’t have metadata for a method like:

\Drupal10::service($type, $id)

Some other constructs available in the metadata file:

@ pattern inside map

@ symbol is replaced with argument value.

override(\IteratorFactory::getFor(0), map('' => '@Iterator')); means if we pass Apple to the method, the return type will be AppleIterator , for Banana it will be BananaIterator , …

type()

override(\array_reverse(0), type(0)); means if we pass a string[] array to the function the return type will be string[] .

/** @var string[] $array */

$array = ['a', 'b', 'c'];

$reversed = array_reverse($array); // $reversed has type "string[]"

elementType()

override(\array_pop(0), elementType(0)); means if we pass a string[] array to the function the return type will be string .

/** @var string[] $array */

$array = ['a', 'b', 'c'];

$el = array_pop($array); // $el has type "string".

The latest releases of PHPStorm also allow providing expected arguments/expected return values for PHP methods. You can check the article here. Also, I recommend you to check the example metadata file inside PHPStorm repository.

Finally, recently I created a module for Drupal which generates a metadata file for your Drupal project — PHPStorm Metadata. It provides a drush command to generate a metadata file in Drupal root, I hope it can be useful.

Conclusion

There are multiple ways to have code completion on Factory style methods: DocBlock comments, Plugins, PHPStorm metadata file. DocBlock is the only one IDE-independent method but requires manual work.

Plugins dynamically analyze the codebase and code completion always stays relevant, but integration with Drupal has a couple of issues.

PHPStorm metadata can be stored in the GIT repository and shared between team members, but someone needs to automate the process of file generation.