At work, I’m currently modernizing a legacy project without composer into an application with less code and thus fewer bugs. The first composer package I required was an application container to add Dependency Injection to the project. The container I used is called orno/di.

I bootstrapped the container in the index file. After that I registered some basic stuff to it like
an object of the Symfony Request class, a Monolog logger with rotating log files, … Finally I was able to adapt
the existing old router to resolve the controllers and their dependencies from the IoC container.

In a couple of hours, I enabled the project to adopt and inject composer-loaded classes within minutes.

Auto resolving

But how does this container, often called an application container, know what to inject? Some containers allow you to configure a hierarchical tree of dependencies in config files or bootstrap code.

Since PHP 5 there is a Reflection extension available. This extension allows you to request metadata about extensions, classes, properties, objects, methods, functions, and arguments. All of these have Reflector classes like ReflectionExtension, ReflectionClass, etc. This Reflection interface allows containers to inspect type hinted arguments of methods and thus constructors.

Constructor Injection

One way to inject a dependent object into a dependent object is to pass it as an argument with the constructor.

You can type-hint arguments of a constructor of a Controller to indicate what type of object this needs.
If the Reflection API tells the container that this constructor needs a GuzzleHttpClient as the first argument, the container can create one by recursively resolving it. If the depending class has depending classes of its own this action of resolving dependencies can be executed recursively.

Interface type hinting

Some types of hints can refer to interfaces. By definition, Interfaces can’t be instantiated. In order to allow the container to know which type to use, that implements the interface, we manually need to hint that to the container. For example with the Laravel container you would do this:

Where VendorClient implements VendorContractsClient.

Method injection

The same action that is used to retrieve the constructor argument types can be used to get the type hints of a callable.
A callable is everything that can be passed as the first argument of call_user_func and call_user_func_array.
The ReflectionFunctionAbstract class allows you to inspect all of these forms of a callable:

Laravel’s Class@method syntax

Laravel’s IoC container also allows you to call Class@method, this will internally not only resolve an object of type Class but also resolve the method‘s dependencies and call it with the right arguments. This effectively does two recursive dependency lookups. It is used in several places like Event::fire('Handler@method', []) but it’s probably
most used for the routes definitions: Route::get('/about', 'Controller@about').

What got added?

In the first version of App::call(...) it was only possible to use Laravel’s Class@method syntax and
anonymous functions (App::call(function (VendorClient $client) {...})).

After my pull request a few weeks ago it is now possible to use every type of PHP callable, including [$object, 'method'].


Enjoy!

The power to inject dependencies into callables

Now that it is possible to add dependencies to every callable and not have to initialize them from where you call it, there are a ton of new possibilities. One of them is the new type of validation that got introduced at Laracon EU last summer:

If your controller method depends on a Request object that also implements the alidatesWhenResolved interface, the FormRequest‘s validate method will be called even before the controller method is called. This helps you to keep your controllers clean.

Other callables

ServiceProvider

When an application that runs on Laravel gets booted it will make use of ServiceProviders to register stuff to the container. All (non-deferred) ServiceProvider‘s register methods will be executed first to bind callables to the container. After that all boot methods will be called.

The callables that get registered to the container using the $app->bind(...) and $app->singleton(...) methods should be able to use the advantage of method dependency injection. For example this is currently a typical way to bind a callable to the container inside the boot method.

If the callables that are registered in the register methods of the ServiceProvider are called with injected dependencies, this could be rewritten to:

Theoretically everything should be bound to the container using the register methods.
That’s why theoretically the boot method could be called using the App::call(...)
functionality to automatically inject dependencies that need extra bootstrapping inside the boot method.

For example in the boot method of the DatabaseServiceProvider one could inject both the IlluminateDatabaseDatabaseManager and the IlluminateEventsDispatcher.

Edit apparently the boot method of ServiceProviders are already being called with method dependency injection. The ServiceProviders just didn’t use this yet.

Other use cases

Following are some other use cases for method injection. These are also not enabled by Laravel. Probably because
every time you depend on App::call it involves adding a dependency on illuminate/container to another Illuminate component.
And that’s generally not a good thing.

Cache::remember

Schema::table

Wrapping

Laravel’s container also features a wrap method.
This wrap method literally wraps any passed anonymous function into a new anonymous function.
When the returned anonymous function gets called the original function is called with
App::call(). This enables you to add method dependency injection everywhere you want on your own.
In the case of Cache::remember:

Other use cases

When plain PHP expects a callable, one could use App::wrap to generate a callable which in turn executes a callable via the App::call functionality. This could be used by Laravel for example to register exception handlers which instantiate Whoops in a more lazy way.

Available containers

There are a number of Containers available in Composer packages. Here are a couple of them that come to my mind:

There are probably a ton of other containers. If you know one that is not on the list:
@hannesvdvreken

Other interesting reads