New .phpstorm.meta.php features

As you may know, besides built-in “code awareness” capabilities, PhpStorm also relies on external knowledge of code. This knowledge comes in the form of PHP stubs and the .phpstorm.meta.php file.

While stubs cover the Standard PHP Library components and common extensions, .phpstorm.meta.php is a special metadata file that you can use for extending the PhpStorm functionality, based on your own needs or project requirements. It is bundled as part of the stubs package (you can check the meta folder to locate it), but it can also be placed anywhere within your project. You can even create multiple metas – PhpStorm will collect and merge all the information from them.

phpstorm-meta-php

In PhpStorm 2019.1, we’ve implemented several metadata features that may come in handy. Read this post for a detailed look into them.

Expected arguments

Let’s start with writing a simple count function like this:

$total = count($array, ...)

If you invoke code completion for the second argument on the above piece of code, initially you’ll get a list of not-so-relevant constants:

count_completion-before

In most cases, however, the second argument should be either the COUNT_NORMAL or COUNT_RECURSIVE constant, corresponding to the 0 and 1 integer values.

You can certainly type a couple of symbols to quickly locate the necessary constant. But shouldn’t there be a way to see them suggested in the first place as the most likely choices? This is where .phpstorm.meta.php comes into play.

In the .phpstorm.meta.php file, you can use the expectedArguments directive, which instructs PhpStorm that a certain function accepts a certain set of arguments.

To deal with our case, let’s add the following record to .phpstorm.meta.php:

expectedArguments(\count(), 1, COUNT_NORMAL, COUNT_RECURSIVE);

Here, we’ve specified the function we are working with, the zero-based index of the argument, and the actual set of expected values. Now, in our initial code piece, all we need to do is invoke code completion again – and the newly added values will be proposed at the very top of the suggestions list:

count_completion-after

When adding your own entries, keep the following in mind:

  • You need to provide the fully qualified name of the function or method so that PhpStorm resolves it correctly.
  • You can enumerate expected arguments via the comma , or the pipe | bitwise operator. The former is used for functions expecting a single value out of the set of values, while the latter is used for functions expecting a bit mask of constants, such as json_encode.

Now for some real-world example.

Let’s say we are implementing a Console command based on the symfony/console component. Sometimes there will be arguments that we want to pass to our command. In this case, we’ll be implementing a Symfony\Component\Console\Command::configure() method:

symfony-console-before

With expectedArguments in place, we could advise PhpStorm to expect the InputArgument::* constants here. To do that, we should add the following line to .phpstorm.meta.php:

If we try autocompletion now, we’ll see the added constants in the suggestions list:

symfony-console-with-expectedarguments

Arguments set

For some functions, the list of possible arguments’ values can be quite large. What’s more, different functions can accept the same sets of values. The ini_get and ini_set functions accepting the php.ini directives can serve as good examples.

Now, if we provide the list of expected arguments for each of these functions by using the above technique, the .phpstorm.meta.php file will grow excessively large. Even worse, it will contain duplicate data, and the amount of work required to maintain it will also double.

To handle this, .phpstorm.meta.php provides two directives, registerArgumentsSet and argumentsSet, which work as follows:

  • registerArgumentsSet accepts two arguments: the arbitrary name of the set of arguments and the list of actual values contained in this set. Values are specified the same way as the list of expected arguments: depending on the function or method you are working with, you can enumerate them via the comma , or the pipe | bitwise operator.

To register the set of arguments for the above case, we simply specify the directive like this:

registerArgumentsSet('ini_values', )

  • Having registered the single set of arguments, we can reference it from within expectedArguments for both functions by using the argumentsSet directive:

expectedArguments(\ini_get(), 0, argumentsSet("ini_values"));
expectedArguments(\ini_set(), 0, argumentsSet("ini_values"));

Expected return values

With the expectedReturnValues directive, you can tell PhpStorm which values a certain function or method returns. The directive is specified similarly to expectedArguments: you provide it with a function and the set of actual values (you can register such sets via registerArgumentsSet, too, of course).

As soon as you do this, PhpStorm will provide code completion for function and static method calls in conditional statements:

json_last_error-completion

Consider another real-world example.

Let’s say you are doing an HTTP request with one of the available PSR-7 compatible clients. As the result, you’ll get a $response object of a class that implements \Psr\Http\Message\ResponseInterface. Here you may want to check the status code and perform the suitable action. It turns out handy to instruct PhpStorm that ResponseInterface::getStatusCode() returns a set of HTTP status codes:

http-request-with-expected-arguments-2

What’s next

Please note that these features are currently experimental and under active development. Since they are in early access, we encourage you to give us your feedback and share what works for you and what you’d like added.

Just as with regular stubs, we will happily accept your GitHub pull requests for .phpstorm.meta.php. If you develop a framework or a library, you can supply it with its own metadata file, too. Add it to your package and with PhpStorm 2019.1 users will get desired autocompletion right away.

In the future, all these capabilities may be reimplemented via PHPDoc (for example, by enhancing the existing @param or @return tags). This could make the entire thing much easier to use and maintain, but naturally, it requires some standardization efforts in the first place. Should this happen at any point, don’t worry, we’ll provide the means of migrating your metas to PHPDoc.

Your JetBrains PhpStorm Team
The Drive to Develop

This entry was posted in Cool Feature, Newsletter and tagged , , . Bookmark the permalink.
  • Marcos

    Is it possible to bundle meta files into PHPStorm plugins?

  • joelharkes

    What about generics? Please add generics.. (Generic types).

  • Dmitry

    Is it possible to use mask for expected constans? InputArgument::* looks great

  • http://www.gander.pl/ Adam Gąsowski

    I am still waiting for a solution to be able to write this way::


    override(DoctrineCommonPersistenceObjectManager::getRepository(0), map([
    ApplicationEntityUser::class => ApplicationRepositoryUserRepository::class,
    ]));

    That would give this effect:


    $entityManager->getRepository(ApplicationEntityUser::class) // ApplicationRepositoryUserRepository

    • Dmitry Tronin

      Do you want to call UserRepository objects via EntityUser this way? We don’t have a feature request to support this. It would be great if you could submit this feature request to our tracker: https://youtrack.jetbrains.com/newIssue

    • http://Rodkom.in.ua Никита

      Man, you have already had this feature! Sure, if you enabled the symfony plugin. I was very surprised to read this and went check. Sure, you have!

  • Кирилл Несмеянов

    It is often necessary to check for a subclass, for example:


    function foo(string $class): SomeInterface
    {
    return new $class(...);
    }

    It would be nice for such cases to offer something like:

    expectedArguments(foo(), 0, classOf(SomeInterface::class));

    • Eugene Morozov

      That looks like something really expensive performance-wise (we either should cache all implementations, or search indexes for them each time on completion), but if you think it’s worth it please submit a feature request: https://youtrack.jetbrains.com/newIssue?project=WI

      • Кирилл Несмеянов

        In this case, the logic should differ very little from the existing. When you click on the interface, the IntelliJ shows all existing implementations for this interface. In this case, the same thing is required, not only for objects (new SomeImpl), but for classes (SomeImpl::class).

        P.S. I do not want to create a task (issue), because stability is more important to me, and the IntelliJ still does not support a lot of standard functional php. In this, it is not worth spending time on functionality that few people need. It is better to finish the language support =)

  • Nicolas Grekas

    Is there some description that would provide auto-completion for keys of arrays?
    E.g. proc_open’s $other_options argument which accept only two keys for now? (“suppress_errors” and “bypass_shell”)

  • Thomas Schulz