Symfony's magic: dependency injection
Tags: symfony, beginners, php, programming
Published: 2023-05-17 11:08:41

Injecting dependencies is a fundamental principle that is recommended by various approaches, including SOLID.

More pragmatically, instead of doing this:

```PHP
namespace App\Services;

class MyService {
    private $myDependency;

    public function __construct()
    {
        $this->myDependency = new MyDependency();
    }
}
```
You can do that (PHP 8):

```PHP
namespace App\Services;

class MyService {

    public function __construct(private MyDependency $myDependency)
{}
}
```
 
However, you may wonder why it's better and how it works behind the scene.

## Injections and autowiring

In a standalone PHP project, you would need to create some class that will have a *single* but quite essential responsibility: read configs and instantiate objects.

Thanks to this class, you would be able to use the shortcut we saw earlier. 

In Symfony, it's usually achieved with built-in interfaces and containers (e.g., `ContainerBuilder`, Dependency Injection component), which saves significant time and efforts.

It's not uncommon to read the term "service container." Symfony looks into the config folder to build its containers. That's why you have to write in specific config files.

Symfony does lots of magic behind the scene, providing features likes **Autowiring**.

By enabling Autowiring, you are telling Symfony to inject dependencies in services automatically:

```yaml
# config/services.yaml
services:
    _defaults:
        autowire: true
    
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Tests,Kernel.php}'
```

[Source: Symfony doc - Autowiring](https://symfony.com/doc/current/service_container/autowiring.html)

It removes the hassle of manual declarations, and classes are "automatically registered as services and configured to be autowired."

This ways, you can load your dependencies _automagically_ and use them in your controllers.

## What's the impact on performances?

Behind the scene, Symfony makes [several optimizations](https://symfony.com/doc/current/components/dependency_injection/workflow.html) we won't see here, including caching and enhanced compilation.

That's why "there is no performance penalty for using autowiring," except on dev environments where the kernel will likely rebuild the container several times.

## Ready-to-use containers

Apps powered by Symfony do not have to bother. The containers contains pretty much all services required.

They only have to type-hint them as arguments to start using them.

For example, if you need logs:

```PHP
namespace App\Service;

use Psr\Log\LoggerInterface;

class MyClass
{
    public function __construct(
        private LoggerInterface $logger,
    ) {
    }
}
```

[Source: Symfony docs - service config into a service](https://symfony.com/doc/current/service_container.html#injecting-services-config-into-a-service)

You can even now specify whether a service should be registered according to the environment thanks to PHP 8 attributes:

 ```PHP
#[When(env: 'dev')]
class MyClass
{
}
```

## Wrap up

There's no ultimate approach, but dependency injection is far better for maintenance than manual coupling.

Symfony simplifies the process and runs major optimizations behind the scene, so you practically get this feature for free (in terms of performances) while respecting standards.