How do I make my PHP IDE understand Dependency Injection Containers?

PhpIdeDependency InjectionPhpstorm

Php Problem Overview


Current situation: I have dependencies in my project that I solve by using dependency injection. I want to take the next logic step by using a dependency injection container (DIC) to ease the management of my dependencies and to lazy-load classes.

I looked at Bucket, Pimple, and sfServiceContainer, ran some test and really appreciate how DIC’s work. I’d probably go for Pimple because of its simplicity and raw power. If I didn’t have this problem:

Due to the abstraction that DIC’s offer, the IDE I’m using (PHPStorm) no longer understands what’s going on in my code. It doesn’t understand that $container['mailer'] or $sc->mailer is holding a class object. I also tried Netbeans IDE: same problem.

This is really a problem for me because my IDE becomes useless. I don’t want to program without code hints, autocompletion and refactoring tools when dealing with classes. And I don’t want my IDE to find all kinds of false positives when validating code.

So my question is: Has anyone dealt with this problem and found a solution?

Php Solutions


Solution 1 - Php

You can define class of the variable 'manually':

/** @var YourClassType $mailer */
$mailer = $container['mailer'];

In PhpStorm (and by standards), use two asterisks and write the data type before the name of the variable.

You can write the data type without the name of the variable (but not the name without the data type).

Solution 2 - Php

While you can certainly tell your IDE the type of the object pulled out of your container every time you access it, it's better to do it once. Both of the following solutions involve subclassing the container. I just started using Pimple which recommends doing this anyway.

For containers that use instance members accessed with -> or exposed via the magic __get method, you can tell your IDE what type they hold. This is great because it doesn't involve any additional parsing when the code is run--only the IDE is bothered by it.

/**
 * My container. It contains things. Duh.
 *
 * @property MyService $service
 * @property MyDao $dao
 */
class MyContainer extends Container { }

For Pimple and other containers that act as arrays you can create accessor functions for the top-level objects you'll need. While it means more parsing when the container is created, it should be done once and kept in APC. I vastly prefer a method over array access anyway since it places the easy-to-forget array key inside an auto-completed method.

class MyContainer extends Pimple
{
    /**
     * @return MyService
     */
    public function getMyService() {
        return $this['service'];
    }
}

BTW, for type-hinting inline variables with @var in NetBeans you need to use /* with one asterisk. This is not a doc-block comment and doesn't work with /** or //. Also, the name comes before the type.

public function foo() {
    /* @var $service MyService */
    $service = $container['service'];
    ...
}

Solution 3 - Php

As the IDE's do not exectue the code, they do not know and need some help form you. I know this works for Eclipse and other IDEs as well: Hint the variable's type.

Netbeans / Phpstorm / PDT / ZendStudio Example

/* @var $mailer MailerInterface */
$mailer = $sc->mailer

Code complete starts to work again on $mailer.

For PDT it's important that:

  1. The comment starts with one * only.
  2. First the variable name, than the hint.

Alternative Comment Variants

As it was subject to a lot of discussion, it can differ between IDEs. However most IDEs support variable hinting for inline code variables in the way above. So depending on the IDE this might be written differently but similar, like here with two asterisks in front:

/** @var $mailer MailerInterface */

PHPDoc compatibility

PHPDoc parsers can have a problem if you mimic the class var doc-comment for inline code as so:

/** @var MailerInterface $mailer  */

That documentation is normally used for class variables (@var - Document the data type of a class variable). PHPDoc is then missing the definition of the class variable after the comment which involves a burden for QA.

However some IDEs will offer code completition for simple variables as well when written in PHPDoc clas-variable style. I do not know if that has side-effects for the code-completition of the current class then as a new member might get introduced that actually does not exists.

Solution 4 - Php

For those who came here from google.

PHPStorm actually provides a way to solve this kind of problem instead of writing PHPDocs over and over again — creating and setting .phpstorm.meta.php file in a way described here gains smoothly working autocomplete and type inspections.

Solution 5 - Php

Pimple just introduce container builder principe. If you understand it, you don't need Pimple any more:


class Container
{
private $shared = array();



public function getService() {
    return new Service(
        this->getFirstDependence(),
        this->getSecondDependence()
    );
}

protected function getFirstDependence() {
    return new FirstDependence(
        this->getSecondDependence()
    );
}

protected function getSecondDependence() {
    return isset($this->shared[__METHOD__]) ? $this->shared[__METHOD__] : $this->shared[__METHOD__] =
    new SecondDependence(
    );
}




}

}

This way Pimple does not hide type of object in mixed $c['some key']. You would have autocomplete suggestions when edit your container. Phpstorm is able to autoresolve method return type from your code. And you would have clear container. You can ever override container:


class TestContainer extends Container
{
protected function getFirstDependence() {
return new FirstDependenceMock(
);
}
}

To be honest container written in 'programming' lanuage is wrong way to go. Container responsibility is to bring initialized graph of objects to caller. Having access to 'programming language' allows to violate that responsibility with easy. Some DSL for configuring dependency is better. Moreover most of original dependency information (argument typehints of constructors) is just ignored by Pimple and sfDepenencyContainer making your configuration bloated and fragile.

Solution 6 - Php

I know that the question is about DIC only, but there is a Silex Pimple Dumper service provider which dumps the container to a json file. The same author wrote a plugin for PHPStorm which can read that file and open the autocomplete with the service names and its type (class, string and etc). I'm using those two components and I can say that are good options for auto completion for Silex/Pimple.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBlaiseView Question on Stackoverflow
Solution 1 - PhpOZ_View Answer on Stackoverflow
Solution 2 - PhpDavid HarknessView Answer on Stackoverflow
Solution 3 - PhphakreView Answer on Stackoverflow
Solution 4 - PhpwersooView Answer on Stackoverflow
Solution 5 - PhppalexView Answer on Stackoverflow
Solution 6 - PhpTales SantosView Answer on Stackoverflow