From Laravel documentation: The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Here we are not going to dig in dependency injection term itself. Just notice that IoC container and dependency injection are very coupled terms. The IoC container is made to make the process of managing dependencies easier and cleaner. Especially it becomes valuable when you deal with complex objects. When your dependencies have their dependencies, and you need to construct all of these objects and it can be a nightmare. You need to remember the way each object should be instantiated. Here is some dummy example of using Dependency Injection to achieve Inversion of Control.
By using constructor injection we have delegated the creation of all the dependencies to the client code. We have achieved clean and testable classes, but we also have achieved a cluttered client code. The client code now has to create all of these objects.
And here the IoC container comes into play. You can declare all your dependencies once in one place, and later simply get them (resolve) out of the container. Instead of managing dependencies ourselves, we delegate them to the container.
Laravel IoC container is the heart of the framework, it keeps all different framework components connected together. It allows them to communicate with each other. Laravel itself is an IoC container. Its Application class extends Container class. Everything that happens inside your application at some point has an interaction with the IoC container. It is the key difference between frameworks and libraries.
The container in Laravel is commonly used to bind and resolve instances of your service providers. I am sure, that if you open any of your service provider classes, you will find bindings there. Something like this:
In service provider we can get the container via
$this->app property. Method
bind registers a binding. The first parameter is a unique
identifier of the binding, for example, a class name or an interface. The second parameter is a callback, that returns an instance of the
binding. This callback will be executed every time we resolve the
Remember, that in this closure we receive the container itself as an argument. Why? It may be useful when we need to resolve some dependencies for our bindings:
Another words, container is a place to store closures that resolve various classes. We can resolve a class anywhere in our application, if it has been registered in the container.
Lets have a look at a real example, how it works in the application. For exmaple we have
As you remember, we have registered
Stripe as the implementation of
Payment contract. Now if we want to change it and
PayPal, we need to change only one line of code in the binding:
Notice, that there is no need to use the container for binding classes, that do not implement interfaces. The container is smart enough to create them. Such classes are constructed with the help of PHP Reflection API. Reflection API is used to inspect classes and methods.
Imagine that in the example above the container does not have a binding for
Stripe. But as the result, we have an
Stripe and also with an instance of
HttpClient injected. Magic? No, its Reflection API:
- First of all, Laravel tries to get a resolver for
Stripe. There is no binding and no resolver for it.
- Next, it examines a constructor of
Stripefor any dependencies recursively and resolves them.
- Finally, it instantiates a new instance of
Stripeand returns it.
But what happens if we try to do the same but with a contract? And imagine that we have no binding for it.
This happens because Laravel does know what implementation you need for this contract. Interfaces cannot be instantiated. Reflection API works only with concrete classes or parameters with a default value.
Shared binding means that this binding should be resolved only once, and the same instance should be returned on subsequent calls to the container. Here I have a dummy example to show how it works:
As you can see, the container instantiates the instance of
sharedInstance binding only once per application lifecycle.
One more way to register a shared binding is method
instance. It will bind an existing object instance into the
container. The given instance will always be returned on subsequent calls into the container.
You can register a binding to the container if it hasn’t already been registered before:
This code will not bound a new object with property
test because there already exists a binding with such alias.
Let’s imagine that you have two classes that use implementations of the same interface, but we need to inject into them different implementations. As we have learned before, we can once register a binding of some implementation and later use it in our code. But in this case, we can use contextual bindings. How does it work? Laravel provides a nice interface for it:
If we need to create a complex object with its own dependencies, we can pass a closure to the
How to find out what has already been registered into the container? If we want to resolve something if we know how it has been bound. But we can group our bindings with tags:
Now we can get all of these bindings with
Sometimes you may need to inject a dependency into one of the bindings. We can do it with the
This method will resolve the binding and execute your closure with the container and the resolved binding as the parameters.
Every time something is being resolved out of the container an event is fired. You can add a listener for this
event with the
resolving method. It accepts a closure with a resolved instance and the container as parameters. You
can attach a handler to listen for any resolved instance or you can pass a class and then typehint a resolved instance: