An example is better than any explaining: Let's say I have the following code
function arrayToIdMap(array $models) : array
{
$result = [];
foreach ($models as $model) {
/** @var BaseClassWithGetIdMethod $model **/
$result[$model->getId()] = $model;
}
return $result;
}
/** @var Car[] **/
$carList = getCarList();
/** @var Truck[] **/
$truckList = getTruckList();
/** @var Car[] **/
$carMap = arrayToIdMap($carList);
/** @var Truck[] **/
$truckMap = arrayToIdMap($truckList);
What I want to do is, to inform PHP automatically the types of $carMap
and $truckMap
so that I have some code that acts like the following pseudocode:
/**
* @param array $$models
* @return The_Original_Type_Inferred_From_$models
*/
function arrayToIdMap(array $models) : array
{
$result = [];
foreach ($models as $model) {
/** @var BaseClassWithGetIdMethod $model **/
$result[$model->getId()] = $model;
}
return $result;
}
/** @var Car[] **/
$carList = getCarList();
/** @var Truck[] **/
$truckList = getTruckList();
$carMap = arrayToIdMap($carList);
$truckMap = arrayToIdMap($truckList);
// $carMap[$id]->someMethod() will not show error if Car class has the someMethod method and PhpStorm knows $carMap is of type Car[] at this point
Is this possible or not?
This could be done with PhpStorm's Advanced Meta functionality (PhpStorm uses it for standard array_xxx
PHP functions -- have a look here: https://github.com/JetBrains/phpstorm-stubs/blob/master/meta/.phpstorm.meta.php#L64
This does not use PHPDoc though. The extra info (advanced metadata) should be stored in a separate file for PhpStorm eyes only.
Make .phpstorm.meta.php
in the project root wit the following content:
<?php
namespace PHPSTORM_META {
override(\arrayToIdMap(0), type(0));
}
Now the file with your code:
<?php
// Defining used classes and functions
class BaseClassWithGetIdMethod {
private string $id;
public function getId(): string
{
return $this->id;
}
}
class Car extends BaseClassWithGetIdMethod {
public function makeCar(){}
}
class Truck extends BaseClassWithGetIdMethod {
public function makeTruck(){}
}
function arrayToIdMap(array $models) : array
{
$result = [];
foreach ($models as $model) {
/** @var BaseClassWithGetIdMethod $model **/
$result[$model->getId()] = $model;
}
return $result;
}
// Test code
/** @var Car[] $carList **/
$carList = getCarList();
/** @var Truck[] $truckList **/
$truckList = getTruckList();
$carMap = arrayToIdMap($carList);
//$carMap[0]->
$truckMap = arrayToIdMap($truckList);
//$truckMap[0]->
With the code above PhpStorm provides correct methods:
If it has to be via PHPDoc ... look at generics.
You would need Psalm (or is it PHPStan?) plugin enabled for that (even if you do not use the actual Psalm) as it is how such a support is supplied here. Anyway, both plugins are bundled with PhpStorm and enabled by default. https://www.jetbrains.com/help/phpstorm/using-psalm.html#generics-support
The code:
<?php
// Defining used classes and functions
class BaseClassWithGetIdMethod {
private string $id;
public function getId(): string
{
return $this->id;
}
}
class Car extends BaseClassWithGetIdMethod {
public function makeCar(){}
}
class Truck extends BaseClassWithGetIdMethod {
public function makeTruck(){}
}
/**
* @template T
* @param T $models
* @return T
*/
function arrayToIdMapZ(array $models) : array
{
$result = [];
foreach ($models as $model) {
/** @var BaseClassWithGetIdMethod $model **/
$result[$model->getId()] = $model;
}
return $result;
}
// Test code
/** @var Car[] $carList **/
$carList = getCarList();
/** @var Truck[] $truckList **/
$truckList = getTruckList();
$carMapZ = arrayToIdMapZ($carList);
$truckMapZ = arrayToIdMapZ($truckList);
$carMapZ[0]->
//$truckMapZ[0]->
That's what I see in my PhpStorm: