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.

While stubs cover the Standard PHP Library components and common extensions, metadata files can be used for extending the PhpStorm functionality based on your own needs or project requirements. The basic metadata file, .phpstorm.meta.php, is bundled inside the PHP stubs package and located inside the meta folder.

Just as PHP stubs, .phpstorm.meta.php is open-source, and you can contribute to its development. You can also package your own metadata file into a public plugin by adding an XML descriptor and uploading the ZIP file to the plugins repository. See the example at https://github.com/JetBrains/phpstorm-library-plugin.

Create metadata files inside your project Do any of the following: Create a php file and name it .phpstorm.meta.php. You can create several such files and store them in different locations within the project. PhpStorm will collect and merge all the information from them.

Create a directory and name it .phpstorm.meta.php. Inside this directory, you can store any number of metadata files and name them arbitrarily. Inside a meta file, declare a PHPSTORM_META namespace and provide the metadata directives. namespace PHPSTORM_META { //metadata directives } Metadata directives specify how a certain function or method should be treated. Directives are written as regular PHP code, which allows for using the existing code editor features such as code completion Ctrl+Space , navigation and usages search, refactorings, and so on.

Navigate to a metadata directive If a function's behavior is altered via the expectedArguments, exitPoint, or override directives, PhpStorm displays the icon in the editor gutter next to the function declaration. Click the icon to navigate to the corresponding directive in a meta file.

Define arguments accepted by a method 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(functionFQN, argumentIndex, ...argumentsList); 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. As an 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: With expectedArguments in place, you can advise PhpStorm to expect the InputArgument::* constants here. To do this, add the following line to the metadata file: 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:

Define possible 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(functionFQN, ...argumentsList); After a function is specified, PhpStorm will provide code completion for function and static method calls in conditional statements. As an 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. You may then 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: namespace PHPSTORM_META { expectedReturnValues(\Psr\Http\Message\ResponseInterface::getStatusCode(), \Symfony\Component\HttpFoundation\Response::HTTP_OK, \Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST, \Symfony\Component\HttpFoundation\Response::HTTP_INTERNAL_SERVER_ERROR ); } When you invoke code completion Ctrl+Space inside a conditional statement, the added constants become available in the suggestions list:

Create named sets of arguments or return values 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 metadata file will grow excessively large. It will also contain duplicate entries, and the amount of work required to maintain it will also double. To handle this, inside a metadata file, you can use two directives: registerArgumentsSet and argumentsSet . The registerArgumentsSet directive 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 as follows: registerArgumentsSet('argumentsSetName', ...argumentsList); Having registered the single set of arguments, you can reference it from within expectedArguments by using the argumentsSet directive: expectedArguments(functionFQN, 0, argumentsSet('argumentsSetName')); As an 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); Having done 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'));

Define exit points By using the exitPoint directive, you can explicitly instruct PhpStorm that the specified function acts as an exit point. The call of such a function is treated as a terminating call, similarly to the calls of the PHP built-in exit or die functions. The directive is specified as follows: exitPoint(functionFQN( [optionalArguments] )); Where optionalArguments can be a string literal or a class constant. If an optional argument is provided, only the function call with this argument will be treated as an exit point. Otherwise, execution will continue normally. As an example, consider the following code sample: class Application { public function terminate($param) { exit(); } public function run() { $this->terminate('foo'); echo "Hello"; $this->terminate('bar'); echo " world!"; } } To mark the terminate('bar') method call as an exit point, add the following line to the metadata file: exitPoint(Application::terminate('bar')); As a result, the terminate call with the bar argument provided will act as an exit point. The last line of the run method will therefore be treated as unreachable:

Define method's return type In many cases, it is not possible to clearly infer the return type of the function based on the function's code itself. By using the override directive, you can explicitly instruct PhpStorm that the specified function returns the entity of a certain type based on the provided arguments. The directive is specified as follows: override(functionFQN, overrideDirective); Where overrideDirective can be one of the following: type: sets the function's return type to the type of the passed argument.

elementType: if the passed argument is an array, sets the function's return type to the type of the elements contained in the array.

map: sets an arbitrary mapping between the argument value and the function's return type. Use the argument's type The type directive instructs PhpStorm that the function's return type matches the type of its argument. The directive is specified as follows: override(functionFQN, type(argumentIndex)); Where argumentIndex is the zero-based index of the argument whose type should be used as the function's return type. As an example, consider the following code sample: class A { public function doActionA($a) { return $a; } } class B { public function doActionB($b) { return $b; } } Initially, you won't get code completion in this and similar expressions: You can address this by adding the following line to the metadata file: override(A::doActionA(0), type(0)); This way, you instruct PhpStorm that the doActionA() method's return type matches the type of its first argument, which is class B in our case. The corresponding code completion entry becomes available: Use the array element's type The elementType directive is applicable for functions that accept an array as their argument. The directive instructs PhpStorm that the function's return type matches the type of the elements contained in the array. Note that it only works for arrays having elements of the same type. The directive is specified as follows: override(functionFQN, elementType(argumentIndex)); Where argumentIndex is the zero-based index of the argument containing an array whose elements' type is used as the function's return type. As an example, consider the following code sample: class A { public function doActionA($a) { return $a; } } class B { public function doActionB($b) { return $b; } } $B1 = new B(); $B2 = new B(); $arrB = [$B1, $B2]; Initially, you won't get code completion in this and similar expressions: You can address this by adding the following line to the metadata file: override(A::doActionA(0), elementType(0)); This way, you instruct PhpStorm that if an array is passed to doActionA() , then this method's return type matches the type of the array elements, which is class B in our case. The corresponding code completion entry becomes available: Provide arbitrary types mapping The map directive lets you set an arbitrary mapping between the argument's value and the function's return type. By using this directive, you can implement generic support for the factory pattern in PhpStorm, and thus get coding assistance when working with common PHP frameworks (such as Magento, Doctrine, Kohana, ZF2, and so on). The directive is specified as follows: override(functionFQN, map([ key => value, ... ])); Where key is a string literal, global constant, or class constant, and value is a ::class constant or a pattern literal. Inside the pattern literal, you can use the @ symbol that will resolve to the literal value of the provided argument. As an example, consider the following code sample. define('myConst', 'GlobalConstant'); class Factory { const classConstant = 'ClassConstant'; public function get($name) { return $name; } } class AClass {} class BClass {} $result = (new Factory())->get(); By using the map metadata directive, you can instruct PhpStorm, which return type is expected from (new Factory())->get() depending on the passed argument: Passing a string literal When the "special" string literal is passed, an instance of \Exception is returned: override(\Factory::get(), map([ "special" => \Exception::class, ])); Passing a global constant When the myConst global constant is passed, an instance of AClass is returned: override(\Factory::get(), map([ \myConst => \AClass::class, ])); Passing a class constant When the classConstant class constant is passed, an instance of BClass is returned: override(\Factory::get(), map([ \Factory::classConstant => \BClass::class, ])); Using lookup patterns The @Class lookup pattern allows for resolving AClass if the 'A' literal is passed, or BClass if 'B' is passed: override(\Factory::get(), map([ '' => '@Class' ]));

Legacy metadata format (deprecated) PhpStorm version 2016.1 and earlier use a different metadata format, which is now deprecated. namespace PHPSTORM_META { $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: 30 July 2020