Tightly Coupled vs Loosely Coupled Classes in PHP

When building PHP applications, understanding coupling is crucial for maintainable and flexible code. Let’s break it down.

1️⃣ Tightly Coupled Classes

A class is tightly coupled when it creates or knows about its dependencies directly. Any change in dependencies requires editing the class itself.

Example:

class PetrolEngine {
    public function start() {
        return "Petrol engine started";
    }
}

class DieselEngine {
    public function start() {
        return "Diesel engine started";
    }
}

class Car {
    private $engine;

    public function __construct($type) {
        if ($type == 'petrol') {
            $this->engine = new PetrolEngine();
        } else {
            $this->engine = new DieselEngine();
        }
    }

    public function startCar() {
        echo $this->engine->start();
    }
}

$car = new Car('petrol');
$car->startCar();

Problem:
If you later introduce ElectricEngine, you must edit the Car class to handle it. This is tightly coupled.


2️⃣ Loosely Coupled Classes (Dependency Injection)

A class is loosely coupled when it doesn’t create its dependencies. Instead, dependencies are injected from outside. This makes code flexible and easier to maintain.

Example:

interface Engine {
    public function start();
}

class PetrolEngine implements Engine {
    public function start() {
        return "Petrol engine started";
    }
}

class DieselEngine implements Engine {
    public function start() {
        return "Diesel engine started";
    }
}

class Car {
    private $engine;

    // Dependency is injected
    public function __construct(Engine $engine) {
        $this->engine = $engine;
    }

    public function startCar() {
        echo $this->engine->start();
    }
}

// Injecting dependencies from outside
$petrolCar = new Car(new PetrolEngine());
$petrolCar->startCar();

$dieselCar = new Car(new DieselEngine());
$dieselCar->startCar();

Benefits:

  • Car doesn’t care about which engine it gets.
  • Adding ElectricEngine doesn’t require editing Car.
  • Code is flexible, testable, and loosely coupled

✅ Key Takeaways

  1. Tightly Coupled: Class creates its dependencies → harder to maintain.
  2. Loosely Coupled: Class receives dependencies → flexible and testable.
  3. Dependency Injection (DI): Main technique to achieve loose coupling.

See if manual way to build DI and service container fails, just use package https://packagist.org/packages/php-di/php-di and you will be good to go with just 2 lines of code!

// inplace of manual let's use library php-id
require 'vendor/autoload.php';
$container = new DI\Container();
$container->get(Car::class);