2

From Zend Docs there is this example of how to use Zend\Di, which is a dependency injection container of Zend Framework:

// inside a bootstrap somewhere
$di = new Zend\Di\Di();

// inside each controller
$movieLister = $di->get('MyMovieApp\MovieLister');

Guidance on how to get $di into your controller is not given in the docs and left to reader's implementation. This could be for example, injecting $di into the controller (hides dependencies), or having $di be global to the application scope (use of globals) or creating $di inside the controller as a local variable (seems least troublesome but hides dependencies). End result is .. $di is inside the controller and ready & available for use.

In the example, use of DiC hides Controller's dependency on MovieLister. You do not know that Controller uses MovieLister by looking at class signature, any setter methods or any class-level parameters.

Why is it okay to "hide dependencies" from classes that call the controller?

DiC seems to be an acceptable design pattern to use these days. What is the correct usage? Why not use it outside of a Controller, i.e. in a ControllerFactory or elsewhere? Can dependency-hiding be avoided with use of DiC?

  • I suspect the answer is "performance." – Robert Harvey Jul 17 '17 at 15:14
  • 6
    You shouldn't do this - this is the service locator (anti)-pattern and should be avoided for the reasons you state. Application code should not need to reference the DI container directly. – Lee Jul 17 '17 at 15:36
  • 1
    I can forgive the use of a global for performance reasons, although I am not sure what performance is gained by using a global, unless you call a lot of controllers in a row, where $di is instantiated once. I am less likely to forgive the dependency-hiding. I am debating whether I want to use a DiC at all, or to use it one-level-up to invoke Controllers with their dependencies. Injecting a DiC into a class would be SL-anti-pattern. I am not certain this is the case here, although I agree it is similar, where injection happens to be done indirectly via a global variable with result being same – Dennis Jul 17 '17 at 16:01
  • In languages I'm familiar with, a DI container will resolve your entire object graph for you, so nothing other than your application root has to access the container. Is that not how it works here? – Ben Aaronson Jul 18 '17 at 15:56
  • the thing is, how is it supposed to work? There is advice out there to not use containers inside controllers, or any classes for that matter, stating that it is an anti-pattern. Then there are use-cases where it is stated that it is okay to use containers to instantiate objects inside a Factory, or for "computing the entry name among a list of entries". i.e., both stated here: php-fig.org/psr/psr-11/meta/… – Dennis Jul 18 '17 at 16:04
3

I don't know how Zend works, but regardless of the framework, hiding the dependencies locks our code to the DI container, obfuscate the code, makes the design opaque and oriented to magic. In general, makes our project harder to understand.

Parameterized constructors and setters make our code less oriented to magic and less prone to Poltergeist. Some containers allow us to drop these wonders from the code, but why should we?1

Regarding the ServiceLocator as alternative

For RAD, when Controllers ask for a lot of dependencies, when business logic is complex enough, instead of cluttering Controller constructor methods with 5+ dependencies, it is at times more convenient to pass the Service Locator / container as the dependency to the Controller and let code inside the Controller invoke various many objects via SL/DiC facilities.

I disagree for the next reasons:

  1. ServiceLocator is still hiding the dependencies and it doesn't make the testing easier either. Quite the opposite.

  2. If the controllers have so many dependencies, don't they have too many responsibilities as well? Is then ServiceLocator hiding my design weakness or just contributing to them?

  3. it is at times more convenient to pass the Service Locator/container as the dependency.

    Where did the supposed abstraction go?

  4. let code inside the Controller invoke various many objects via SL/DiC facilities.

    Where did IoC go?

What is the container doing for us? If we use the container here and there, where is the consistency?

In Java, Spring does the very same magic, but it doesn't prevent me from implementing constructors and setters and enforce the framework to use them all. My reasoning is that the more I fall into the Spring facilities the more I lock my developments to the framework. Ultimately, I'm not a Spring senior engineer.

Summarising, don't hide your dependencies. Make them obvious and obvious will be your designs and don't lock yourself to a specific tool.


1: This is the sweet we should avoid. It generates a "personal" technical debt that eventually reverts against you.

  • is other words, don't use Dependency-injection Containers, including their use for Rapid Application Development? – Dennis Jul 18 '17 at 13:37
  • Not exactly.Use them if 1. You need productivity, 2. you can get abstracted totally from the DI. If you have to invoke manually the container or the ServiceLocator, then the framework may not be for you. If your dependency graph is too complex, may be your design is asking you for a review. Too many dependencies often mean too many responsibilities as well. Keep declaring constructors and setters and enforce the container to use them. It's your design, your code, your rules so don't constraint your code to the framework limitations or facilities. Don't lock yourself to them. – Laiv Jul 18 '17 at 13:51
  • I could add some examples, but they would be in Java and Spring, I don't know if that may help you. – Laiv Jul 18 '17 at 13:52
  • So if your framework supports DI by default (like Zend's ServiceManager), I understand it is okay to use it (via configuration or otherwise) as long as the framework handles the initialization and maintenance of the container, as long as you yourself are not instantiating a new DI yourself? I can read Java code but I am not familiar with Spring – Dennis Jul 18 '17 at 13:57
  • Dice. I have shared a Dice example of DI which I think is closer to my point of view about how IoC should be. Note that class A is totally agnostic to $dice. If class A had to know about $dice to initialize B, there would not be IoC (IMO). Note also that A has a parametrized constructor (that's good!) despite It could just invoke $dice directly. So, regarding your previous comment. Yes, that's my point. Eventually, $dice calls should be somewhere in the code. For instance within factories. – Laiv Jul 18 '17 at 21:20
0

Don't use them

...unrestricted. But do use them when their use is restricted to a specific well-maintained role as doing so will ease software maintenance and avoid dependency-hiding.

First, some background.

In context of Zend Framework 2+ architecture, injecting any DiC into a Controller means using that DiC as Service Locator. (In Zend Framework, concrete implementation of Service Locator is called ServiceManager).

For pure use of DiC as DiC there is a Register Resolve Release (RRR) pattern, which creates the Controller for you and populates it with dependencies. You do not, as a rule, inject DiC itself into the controller, although you could if you so wanted to, thereby mixing DiC and Service Locator patterns.

This link is helpful in providing more details on the differences: https://github.com/aimfeld/ZendDiCompiler

Community Advice

Also, there has been some advice to avoid using DiC, because of slower PHP Reflection API and various maintenance issues down the road. DiC makes it easy to use complex dependencies resulting in complex dependency graphs that are created automatically without explicit manual code wiring. This makes it hard to debug them when things go wrong. If new dependencies are added over time, automatic rewiring is done for you without explicit wiring code being written. This potentially makes it harder to track dependencies manually when something goes wrong. Using Service Locator pattern is then recommended as it restricts you to Factory-based Di, giving you more explicit control of Di via explicit configuration.

For RAD, when Controllers ask for a lot of dependencies, when business logic is complex enough, instead of cluttering Controller constructor methods with 5+ dependencies, it is at times more convenient to pass the Service Locator / container as the dependency to the Controller and let code inside the Controller invoke various many objects via SL/DiC facilities.

For non-RAD, break up your controllers to where they do not have that many dependencies.

Also:

"Besides ZF2 controllers, I recommend not to inject ZendDiCompiler directly anywhere. If you need a service in one of your classes, just ask for it in the constructor"

From: dependency-injection-container-vs-service-locator

However, I would not recommend injecting DiC into Controllers. Read below as to why.

Using Service Locator

Do not use Service Locator unrestricted. It has been deprecated in Zend for good reasons.

Use restricted version of Service Locator as for example how it is being used in Zend Framework version 3. It is restricted to being a Factory-based hardcoded-configuration Service Locator implemented via ServiceManager class. It connects Routing to Controllers via instantiation of Controllers directly, or Factories (where Controllers receive their dependencies), Invokables, AbstractFactories. See More about ServiceManager here

Using Dependency-injection/Inversion of Control Containers

You can use DiC for ease of dependency management for Rapid Application Development (RAD), but expect more debugging and maintenance issues down the road if you don't subsequently refactor to inject your dependencies directly. To avoid greater pains, if you are going to use DiC, use the RRR pattern to create your Controllers and populate them with any dependencies.

PSR-11 Container Interface

See http://www.php-fig.org/psr/psr-11/#13-recommended-usage

  • Also, since Controllers typically ask for a lot of dependencies, when business logic is complex enough, instead of cluttering Controller constructor methods with 5+ dependencies, it is at times more convenient to pass the Service Locator / container as the dependency to the Controller and let code inside the Controller invoke various many objects via SL/DiC facilities. how do this affect testing? – Laiv Jul 17 '17 at 20:31
  • I reworded that snippet for RAD and non-RAD. But .. if affects testing in how you wire up your testing harness. Testing is more flexible when you have explicit dependencies defined that you can pass to your classes. If you have Service Locator inside your class, you define and pass Service Locator to your test harness. I see it as possibly having a different configuration/set up of the testing harness. Also, "One side effect of dependency hiding is that it makes testing more difficult" - mwop.net/blog/2016-04-26-on-locators.html – Dennis Jul 17 '17 at 21:06
  • 1
    What I try to say is that you are hidding the dependencies throught the ServiceLocator, so the unit tests is somewhat tricky., because you will end up mocking Up the 5+ dependencies somehow... Not a big deal better improvement. – Laiv Jul 17 '17 at 21:10
  • 1
    Plus, if your controllers have so many dependencies It probably they have too many responsabilities. Aren't they? Don't look for the tool that better hide your poor design. Look for the one that expose your weakness so that you can improve them. – Laiv Jul 17 '17 at 21:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.