[Before I start, let me say that I'm mostly a Java programmer - with only a little bit of PHP knowledge. But I'll simply try to get the most important concepts across without language specifics.]
Dependency Injection is based on two parts of code:
- Construction
- Execution
In its most extreme shape, there are no new
operators to be found in the Execution part. All of them are moved into the Construction part. (In practice, this will be toned down.)
All of the construction happens - in the Construction part. It creates the graph of objects needed for Execution bottom up. So let's assume, it should construct A:
- A depends on B, and
- B depends on C.
Then
- C is constructed first.
- Then B is constructed with C as a parameter.
- Then A is constructed with B as a parameter.
So C doesn't have to be passed as a constructor parameter to A. This small example doesn't illustrate strongly enough, how much this reduces the amount of objects that have to be passed around to quite a small number.
The Dependency Injector itself should not be passed into the Execution part. This is one of the basic mistakes everyone (including myself) tries to make, when they first come in contact with DI. The problem is, that this would completely blur the lines between Construction and Execution. Another way to say it is, that it would violate the Law of Demeter. Or in pattern speak: It would eventually "degrade" the Dependency Injection pattern to the Service Locator pattern. It's debatable, if this is really a degradation, but in any case it's usually not a good idea to misuse the Dependency Injector as a Service Locator.
So whenever you need to give one of your constructed objects the capability to produce other objects during execution, instead of passing the Dependency Injector, you would only pass simple Providers (a term used by the Java DI framework Guice). These are rather simple classes that can only create a certain kind of object. They have similarities with a factory.
First try to pass the required dependencies directly to the constructor.
So, to sum it up:
- Build objects bottom-up.
- Only pass as few dependencies as required to create an object.
- Once your done, start executing.
- During execution, you can still fetch newly created objects by using Providers.
But don't take it too far: Simple objects can still be created without a Provider :-)
And now, all you'll have to do is to translate this stuff into quality code. Maybe others can help you out with a few PHP examples.
Addendum: A little bit more about Providers
As noted above, the notion "Provider" (a specialized factory) is a bit specific to the Java DI framework Guice. This framework can automatically create a Provider for any type of object. However, the concept is generally useful for DI. The only difference is, that without the help of Guice or a similar framework, you'll have to write the Providers yourself - but that's quite easy:
Let's say, B depends on C.
- If B just needs one fixed instance of C, then you don't need a Provider - you can simply construct B with the constructor argument C.
- If B needs to create more instances of C during execution, then just write a class called
CProvider
with a get()
method, that can create a new instance of C. Then pass an instance of CProvider
into the constructor of B, and store the Provider in an instance field of B. Now B can call cProvider.get()
when it needs a new instance of C.
Providers are part of the Construction code, so you're allowed to use new C(...)
! On the other hand, they're not part of the Execution code, so you shouldn't have any execution logic there.
CProvider
can be passed into multiple constructors of course. You can also write multiple versions CProvider1
, CProvider2
, ... - where each can construct different versions of C objects with different properties. Or you simple instantiate CProvider
multiple times with different arguments.