PHPStorm Auto-complete Array Keys (dynamically inserted)

4.1k views Asked by At

I'm using Pimple dependency injector, and every time I use a dependency from the container, I can't help but to double check the spelling of the key used to get the dependency:

$ioc = new Pimple();

// 1. Define some object
$ioc["some-key"] = $ioc->share(function($c){ /* ... */});

// 2. Use it
$ioc["som... // Open config file and check spelling...

Does PHPStorm have some way of looking up those properties and providing auto-completion? I have considered defining all those keys using something like

define('SOME_KEY', 'some-key');

// ...

$ioc[SOME_KEY] = $ioc->share(/* ... */);

but I wonder if there's a better way.

Edit

Here's some sample code:

// project_root/library/App/Injector/Ioc.php
require_once "Pimple.php";

/** @var array|Pimple $ioc */
$ioc = new Pimple();

$ioc["version"] = "1.0.1650.63";

$ioc["location-service"] = $ioc->share(function ($c) {
     return new Application_Service_Location();
   }
);

It turns out that string auto-completion works fine whether or not I include /** @var array|Pimple $ioc */ before the $ioc declaration in the same file as $ioc is declared. However, since I'm using Zend Framework, I'm usually using $ioc thusly:

// project_root/Application/Bootstrap.php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
   protected function _initInjector() {
     $ioc = null;
     require_once LIBRARY_PATH . "/MFM/Injector/ioc.php";
     Zend_Registry::set("ioc", $ioc);
   }
}

// project_root/Application/Controllers/SomeController.php
class Application_Controller_SomeController extends Zend_Controller_Action {
   public function IndexAction() {
      /** @var Pimple $ioc */
      $ioc = Zend_Registry::get("ioc");

      // No IDE assistance for the string "location-service"
      $service = $ioc["location-service"];
   }
}
2

There are 2 answers

0
Anatoly U On BEST ANSWER

I currently use the DynamicReturnTypePlugin for PhpStorm and have the following config of it in my dynamicReturnTypeMeta.json:

{
    "methodCalls": [
        {
            "class": "\\MyOwnWrapperOfDIContainer",
            "method": "get",
            "position": 0
        },
        {
            "class": "\\Pimple\\Container",
            "method": "offsetGet",
            "position": 0
        }
    ]
}

This configurations means the following: whenever in code I'm using any variable of type Pimple\Container as an array (this will call its ArrayAccess::offsetGet method), then PhpStorm should consider the return value is of type that was used as a key when accessing that array. i. e.:

$c = new Pimple\Container;
$c['MyClass']->/*here autocompletion works*/methodOfMyClass();

I also use it in that way:

$myClassInstance = MyOwnWrapperOfDIContainer::get(MyClass::class);
$myClassInstance->methodOfMyClass(); // autocompletion works here

The only problem is when you register some dependencies in Pimple container not by their class name, but using other names which you want. E. g., autocompletion won't work in the following case:

$c = new Pimple\Container;
$c['my-favourite-var'] = new MyClass(1);
$c[MyClass::class] = new MyClass(2);
$c['my-favourite-var']->/*here autocompletion doesn't work*/methodOfMyClass();
$c['MyClass']->/*here autocompletion works*/methodOfMyClass();

Still you can workaround it like this:

class MyFavouriteVar extends MyClass;
$c[MyFavouriteVar::class] = new MyFavouriteVar(2);
// or even like this:
$c[MyFavouriteVar::class] = new MyClass(2);
$c[MyFavouriteVar::class]->/*now autocompletion works fine*/methodOfMyClass();

Or you have to find some other solution of your problem...

edit 1

also consider this article: http://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata

edit 2

and this plugin https://github.com/Sorien/silex-idea-plugin

edit 3

Also it's possible to workaround the problem mentioned above like this:

class MyPimple extends Pimple\Container
{
    public function get($type, $desc = null) {
        return $this[$type . (isset($desc) ? ':' . $desc : '')];
    }
}
$c = new MyPimple;
$c[MyClass::class] = new MyClass('default');
$c[MyClass::class . ':' . 'my-special-value'] = new MyClass('special');
$c->get(MyClass::class, 'my-special-value')->/*autocompletion should work*/methodOfMyClass();

The dynamicReturnTypeMeta.json should then contain:

{
    "methodCalls": [
        {
            "class": "\\MyPimple",
            "method": "get",
            "position": 0
        }
    ]
}
0
Lebnik On

Put the file ".phpstorm.meta.php" into root of your repository. Content of file .phpstorm.meta.php:

<?php
namespace PHPSTORM_META {
    override( \Container::get(0), map([]));
}

Where \Container::get - method for get some object, example:

\Container::get('My\Super')

or

\Container::get(My\Super::class)

and now you can use autocomplete of all class methods.