Skip to main content
Home
Drupal life hacks

Main navigation

  • Drupal
  • React
  • WP
  • Contact
  • About
User account menu
  • Log in

Breadcrumb

  1. Home

Universal “Soldier” in PHP. Dependency Injection Without Pain (Symfony, Laravel, Drupal)

By admin, 26 December, 2025
Illustration of a "Universal PHP Soldier" wearing armor with PHP, Symfony, Laravel, and Drupal logos. The soldier holds a laptop displaying "<DI>" representing Dependency Injection and a wrench representing controllers. Flow arrows show Controller → Mailer Service → Response. The image emphasizes mastering DI across PHP frameworks.

Universal “Soldier” in PHP

Dependency Injection Without Pain (Symfony, Laravel, Drupal)

In the first article, we covered the controller as a universal entry point into any modern PHP framework.
Now it’s time for the next critical step — Dependency Injection (DI).

Many developers fear DI because:

  • “There’s too much magic”
  • “I don’t understand where objects come from”
  • “In Drupal it’s a nightmare”

The good news is simple:

👉 Dependency Injection works the same way in all modern PHP frameworks.
Only the syntax and level of automation differ.


What Is Dependency Injection (in plain English)

Bad (tight coupling):

class OrderService {
    public function __construct() {
        $this->mailer = new Mailer();
    }
}

Good (Dependency Injection):

class OrderService {
    public function __construct(Mailer $mailer) {
        $this->mailer = $mailer;
    }
}

Core idea:

A class should not create its own dependencies — it should receive them from the outside.


Universal Rule #2

If you understand Dependency Injection in Symfony, you understand it in Laravel and Drupal.

Why?
Because all of them use a Service Container.


1. Symfony — the Reference Implementation

Symfony exposes DI clearly and explicitly. Nothing is hidden.

Service Example

// src/Service/Mailer.php
namespace App\Service;

class Mailer {
    public function send(string $msg): void {
        // send email
    }
}

Injecting the Service into a Controller

use App\Service\Mailer;
use Symfony\Component\HttpFoundation\Response;

class HelloController
{
    public function __construct(
        private Mailer $mailer
    ) {}

    public function index(): Response
    {
        $this->mailer->send('Hello');
        return new Response('OK');
    }
}

What’s happening here

  • Symfony automatically registers the service
  • Stores it in the container
  • Injects it by type-hint (autowiring)

📌 Autowiring by type is the key concept.


2. Laravel — Same DI, More Sugar

Laravel may look different, but under the hood it’s the same idea.

Service

namespace App\Services;

class Mailer {
    public function send(string $msg): void {}
}

Controller

use App\Services\Mailer;

class HelloController extends Controller
{
    public function __construct(
        private Mailer $mailer
    ) {}

    public function index()
    {
        $this->mailer->send('Hello');
        return 'OK';
    }
}

Differences from Symfony

  • Services don’t need explicit registration
  • Laravel auto-discovers classes
  • Fewer configuration files

📌 Laravel = Symfony DI with friendly defaults.


3. Drupal — DI with Heavy Armor

Drupal scares many developers, but once stripped down, it’s still Symfony DI.

Service in Drupal

// modules/custom/mymodule/src/Mailer.php
namespace Drupal\mymodule;

class Mailer {
    public function send(string $msg): void {}
}

Service Registration (Required)

# mymodule.services.yml
services:
  mymodule.mailer:
    class: Drupal\mymodule\Mailer

Controller with DI

use Drupal\mymodule\Mailer;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\DependencyInjection\ContainerInterface;

class HelloController {

    public function __construct(
        protected Mailer $mailer
    ) {}

    public static function create(ContainerInterface $container): self {
        return new static(
            $container->get('mymodule.mailer')
        );
    }

    public function index(): Response {
        $this->mailer->send('Hello');
        return new Response('OK');
    }
}

Why so verbose?

  • Backward compatibility
  • Explicit YAML configuration
  • Strict control over services

📌 The concept is identical, only the ceremony differs.


Same Concept, Three Syntaxes

FrameworkService RegistrationInjection Method
SymfonyAuto / services.yamlConstructor
LaravelAutoConstructor
Drupalservices.ymlcreate() + constructor

The Most Common Mistake

❌ Don’t do this:

\Drupal::service('mailer');
app(Mailer::class);

❌ And don’t do this:

new Mailer();

✔️ Do this instead:

public function __construct(Mailer $mailer)

The Universal Pattern

Memorize this — it works everywhere:

class AnyClass {
    public function __construct(
        private Dependency $dependency
    ) {}
}

If a framework supports this — you’re home.


Conclusion

  • DI is not a Symfony-only feature
  • DI is not Laravel magic
  • DI is not a Drupal punishment

It’s a core architectural principle, expressed differently across frameworks.

Master it once — and you stop being afraid of any PHP framework.

Tags

  • PHP
  • Symfony
  • Laravel
  • Drupal
  • Dependency Injection
  • DI
  • PHP frameworks
  • PHP controller
  • Symfony controller
  • Laravel controller
  • Drupal controller
  • Service Container
  • Autowiring
  • PHP tutorial
  • PHP architecture
  • Backend Development

Comments

About text formats

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Powered by Drupal