---
title: Use Dependency Injection
impact: HIGH
impactDescription: enables testability and loose coupling
tags: dependency-injection, testing, coupling, architecture, quality, php
---

## Use Dependency Injection

Direct instantiation creates tight coupling, making testing difficult and changes risky. Dependency Injection (DI) allows for mockability, replaceability, and better overall architecture.

**Incorrect (hardcoded dependencies):**

```php
class OrderService {
    private Database $db;
    private Mailer $mailer;

    public function __construct() {
        // Hardcoded dependencies - impossible to mock in unit tests
        $this->db = new MySqlDatabase();
        $this->mailer = new SmtpMailer();
    }

    public function createOrder($data) {
        $this->db->save($data);
        $this->mailer->send($data['email'], 'Order created');
    }
}
```

**Correct (injected dependencies via constructor):**

```php
interface DatabaseInterface {
    public function save(array $data);
}

interface MailerInterface {
    public function send(string $to, string $msg);
}

class OrderService {
    // PHP 8+ Constructor Property Promotion
    public function __construct(
        private DatabaseInterface $db,
        private MailerInterface $mailer
    ) {}

    public function createOrder(array $data) {
        $this->db->save($data);
        $this->mailer->send($data['email'], 'Order created');
    }
}

// In production (using a DI Container like Laravel Service Container or Symfony)
$service = $container->get(OrderService::class);

// In Unit Tests
$mockDb = $this->createMock(DatabaseInterface::class);
$mockMailer = $this->createMock(MailerInterface::class);
$testService = new OrderService($mockDb, $mockMailer);
```

**Benefits:**
- **Testability**: Easily swap real services for mocks or stubs.
- **Maintainability**: Centralized control over dependency lifetimes and configurations.
- **Flexibility**: Change implementations (e.g., switch from SMTP to SendGrid) without modifying business logic.
- **CLarity**: Class dependencies are explicitly declared in the constructor.

**Tools:** PHPUnit, Mockery, Laravel/Symfony Containers, PHPStan
