PhpStorm 2023.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.

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.

Create metadata files inside your project

  1. 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.

  2. 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.

    Code Completion in a Meta File
  • If a function's behavior is altered via the expectedArguments, exitPoint, or override directives, PhpStorm displays the the Metadata icon icon in the editor gutter next to the function declaration.

    Click the the Metadata icon icon to navigate to the corresponding directive in a meta file.

    Meta Indicator in the Editor Gutter

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:

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 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 suggestion list:

Code completion before Expected Arguments is set

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.

Meta content and code completion for json_last_error

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 suggestion list:

Code completion for http request with expected return values

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:

Exit Point Method Call

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:

  • functionFQN is the fully qualified name of a function or a class method. The __get(), __call(), and __callStatic() magic methods are also supported.

  • overrideDirective is 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:

Method return type not overridden

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:

Method return type overridden

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:

Method return element type not overridden

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:

Method return element type overridden

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. You can also provide union types, that is, several types separated by the pipe | symbol.

As an example, consider the following code sample.

define('myConst', 'GlobalConstant'); class Factory { const classConstant = 'ClassConstant'; public function get($name) { return $name; } public function __call($name, $arguments) {} } class AClass { public function doActionA() {} } class BClass { public function doActionB() {} } $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. You can also specify the return types for methods invoked via the __call() magic method.

Pass a string literal

When the "special" string literal is passed, an instance of \Exception is returned:

override(\Factory::get(), map([ "special" => \Exception::class, ]));
Method return type overridden via string literal

Pass a global constant

When the myConst global constant is passed, an instance of AClass is returned:

override(\Factory::get(), map([ \myConst => \AClass::class, ]));
Method return type overridden via global constant

Pass a class constant

When the classConstant class constant is passed, an instance of BClass is returned:

override(\Factory::get(), map([ \Factory::classConstant => \BClass::class, ]));
Method return type overridden via class constant

Use 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' ]));
Method return type overridden via literal

Construct a dynamic return type for a magic method

The @Class lookup pattern allows for resolving AClass if the a() method is called, or BClass if b() is called:

override(\Factory::__call(), map([ '' => '@Class' ]));
Method return type overridden via literal

Use union types

The AClass|BClass union type declaration allows for resolving both AClass and BClass:

override(\Factory::get(), map([ '' => 'AClass|BClass' ]));
Method return type overridden via literal

Set up dynamic prefixes for table names in SQL language injections

Many content management systems and frameworks support the use of table prefixes, which are defined at the application configuration level. In the code, tables' names are specified with prefix markers within SQL strings. When the database layer of the application processes these strings, such names are replaced with the actual table names including a prefix.

By using the sql_injection_subst metadata directive, you can set up the rules for replacing the prefix markers with actual prefixes in injected SQL strigs. As a result, PhpStorm will provide code completion for the database tables' names as well as use them when running SQL queries.

The directive is specified as follows:

override(sql_injection_subst(), map([ "prefix_marker" => "replacement" ]));

Consider the following example: the database contains two tables, app_users and app_products, which are using the app_ table prefix. In the code, the [prefix ] and #__ prefix markers are used to refer to these tables. Initially, the tables' names in injected SQL strings are not recognized, so code completion for them is not available.

Unknown table name in SQL injection

To register the database prefixes, add the following to the metadata file:

override( sql_injection_subst(), map( [ "[prefix]" => "app_", "#__" => "app_" ]));;

As a result, PhpStorm automatically substitutes the prefix marker with the actual prefix and tables' names become available:

Resolved table name in SQL injection

Legacy metadata format (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: 25 March 2024