PhpStorm 2019.3 Help

PhpStorm advanced metadata

Besides built-in “code awareness” capabilities, PhpStorm relies on external knowledge of code, which comes in the form of PHP stubs and the special advanced metadata files .phpstorm.meta.php.

While stubs cover the Standard PHP Library components and common extensions, .phpstorm.meta.php can be used for extending the PhpStorm functionality based on your own needs or project requirements. The basic metadata file is bundled inside the PHP stubs package and located inside the meta folder. You can create multiple meta files and place them anywhere within your project – PhpStorm will collect and merge all the information from them.

In metadata files, regular PHP code is used as a configuration means, which allows using the existing APIs to analyse it. The specific format is chosen to facilitate the existing editor features, such as code completion, navigation and usage search, refactorings, and so on.

Expected arguments

The expectedArguments directive instructs PhpStorm that a certain function accepts a certain set of arguments. The directive is specified by providing the function you are working with, the zero-based index of the argument, and the actual set of expected values, as follows:

expectedArguments(functionName, argumentIndex, ...argumentsList);

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.

Example

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

Code completion before Expected Arguments is set

With expectedArguments in place, you can advise PhpStorm to expect the InputArgument::* constants here. To do this, add the following line to .phpstorm.meta.php:

expectedArguments( \Symfony\Component\Console\Command\Command::addArgument(), 1, \Symfony\Component\Console\Input\InputArgument::OPTIONAL, \Symfony\Component\Console\Input\InputArgument::REQUIRED, \Symfony\Component\Console\Input\InputArgument::IS_ARRAY );

Now, when you invoke code completion Ctrl+Space, the added constants are displayed in the suggestions list:

Code completion before Expected Arguments is set

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. If you provide the list of expected arguments for such functions, the .phpstorm.meta.php file will grow excessively large. It will contain duplicate data, and the amount of work required to maintain it will also double.

To handle this, inside a .phpstorm.meta.php file, you can use two directives, registerArgumentsSet and argumentsSet.

  • 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, specify the directive like this:

    registerArgumentsSet('argumentsSetName', ...argumentsList);
  • Having registered the single set of arguments, you can reference it from within expectedArguments by using the argumentsSet directive:

    expectedArguments(functionName, 0, argumentsSet("argumentsSetName"));

Example

Consider the ini_get and ini_set functions both accepting the same set of php.ini directives.

You can register the set of arguments as follows:

registerArgumentsSet('ini_values', ...iniValuesList);

After that, you can reference this set from within expectedArguments both for ini_get and ini_set:

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

Expected return values

The expectedReturnValues directive instructs 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 (such sets can also be registered via ArgumentsSet) as follows:

expectedReturnValues(functionName, ...argumentsList);

After a function is specified, PhpStorm will provide code completion for function and static method calls in conditional statements.

Meta content and code completion for json_last_error

Example

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

Code completion for http request with expected return values

Factory methods

The factory design pattern is commonly used in PHP frameworks (such as Magento, Doctrine, Kohana, ZF2, and so on). By using a metadata file, you can implement generic support for the factory pattern in PhpStorm.

Suppose you have something like the following "service" or "helper" provider of interfaces/objects.

interface ServiceLocatorInterface { function get($name); //You call this to get your helper depending on argument function getByPattern($name); } class ServiceManager implements ServiceLocatorInterface { function get($name) { return new $name; //simplest test example implementation   } function getByPattern($name) { return new $name; } } $serviceManager = (new ServiceManager()); function globalFactoryFunction($param) { return new $param; }

You can make the following cases operable by using the additional metadata file.

//(1) static factory call $getStatic = ServiceManager::get("special"); //(2) non-static factory $getDynamic = $serviceManager->get("special"); //(3) ArrayAccess style factory $getFromArray = $serviceManager["special"]; //class name constant $getByClassNameConst = $serviceManager->get(Exception::class); $getByClassNameConstFromArray = $serviceManager["Exception"]; //"fallback" to direct class name $getWithFallback = $serviceManager->get("Exception"); $getWithFallbackArray = $serviceManager["Exception"]; //look up by expressions $byPattern = $serviceManager->getByPattern("Seekable" /*Iterator*/); //(4) $getFromStaticFunction = globalFactoryFunction("Exception"); //this is all resolved and green! $getByClassNameConst->getCode(); $getWithFallback->getCode(); $getByClassNameConstFromArray->getCode(); $getFromArray->getCode(); $getDynamic->getCode(); $byPattern->seek(1); class Foo { /** * @var ServiceManager **/ protected $serviceManager; function test() { $worksThroughProperties = $this->serviceManager->get(Exception::class); $worksThroughProperties->getCode(); // same in class } }

The .phpstorm.meta.php for this case should look as follows:

namespace PHPSTORM_META { // we want to avoid the namespace pollution //You can use QuickDoc or even Go to definition on these "magic" functions override(\ServiceLocatorInterface::get(0), // method signature //argument number is ALWAYS 0 now. map([ //map of argument value -> return type "special" => \Exception::class, //Reference target classes by ::class constant \ExampleFactory::EXAMPLE_B => ExampleB::class, // we can now support class constant argument values \EXAMPLE_B => \ExampleB::class, // and global constants too //non-mapped value, such as $getByClassNameConst case above will be returned automatically ])); //pattern example. `@` is replaced by argument literal value. override(\ServiceLocatorInterface::getByPattern(0), map([ '' => '@Iterator|\Iterator', ])); //basicaly the same as get, just for array["arg"] lookups override(new \ServiceLocatorInterface, map([ "special" => \Exception::class, ])); }

Legacy metadata format (deprecated)

namespace PHPSTORM_META { // we want to avoid the pollution // this is legacy format for 2016.1 and EARLIER // This file is not a CODE, it makes no sense and won't run or validate // Its AST serves IDE as DATA source to make advanced type inference decisions. $STATIC_METHOD_TYPES = [ // we make sections for scopes \ServiceLocatorInterface::get('') => [ // STATIC call key to make static (1) & dynamic (2) calls work "special" instanceof \Exception, // "KEY" instanceof Class maps KEY to Class ], new \ServiceLocatorInterface => [ // NEW INSTANCE is to make ArrayAccess (3) style factory work "special" instanceof \Exception, ], \ServiceLocatorInterface::getByPattern('') => [ "" == "@Iterator", // "ignored" == "PatternWith@" substitutes @ with arg value ], \globalFactoryFunction('') => [ // (4) works also with functions ], // if key is not found its used as type name in all cases ]; }
Last modified: 2 December 2019