Skip to main content

Dependency Injection (DI)

Chapter Overview

In this chapter you will learn:

  • What Dependency Injection is
  • Why DI exists
  • Problems without DI
  • Tight Coupling
  • Loose Coupling
  • Constructor Injection
  • Method Injection
  • Property Injection
  • Dependency Inversion Principle
  • Service Containers
  • Laravel Examples
  • PocketMine Examples
  • ZyroNetwork Architecture
  • Best Practices
  • Enterprise Patterns

Introduction

Dependency Injection is one of the most important concepts in modern software engineering.

Frameworks like:

  • Laravel
  • Symfony
  • PocketMine-MP
  • Spring Boot
  • ASP.NET

all heavily rely on Dependency Injection.

Without DI:

Large applications become difficult to maintain,
difficult to test,
and impossible to scale properly.

What is a Dependency?

A dependency is simply:

Something a class needs
in order to work.

Example:

class UserService {

}

UserService may need:

  • Database
  • Logger
  • Config
  • Cache

These are dependencies.


Real Life Example

Imagine a car.

A car depends on:

  • Engine
  • Wheels
  • Fuel

Without them:

The car cannot function.

Similarly:

UserService

depends on:

Database
Logger
Cache

Problem Without Dependency Injection

Example:

class UserService {

private Database $database;

public function __construct()
{
$this->database =
new Database();
}

}

This works.

But it creates a huge problem.


Problem: Tight Coupling

Visualization:

UserService

Database

The service is directly connected to Database.

You cannot replace it.


Problems Created

❌ Hard to test

❌ Hard to replace database

❌ Hard to scale

❌ Hard to maintain

❌ Violates SOLID principles


Example

Suppose later:

MySQL → MongoDB

Now you must rewrite everything.


Dependency Injection Solution

Instead of creating dependencies yourself:

new Database();

Receive them from outside.


Example:

class UserService {

public function __construct(
Database $database
) {

}

}

Now:

Someone else creates Database.

Visualization

WITHOUT DI:

UserService

new Database()

WITH DI:

Application

Database

UserService

Much better.


Definition

Dependency Injection means:

Providing dependencies from outside
instead of creating them internally.

Types of Dependency Injection

There are three major types:

  1. Constructor Injection
  2. Method Injection
  3. Property Injection

Constructor Injection

Most common.


Example:

class UserService {

public function __construct(
private Database $database
) {

}

}

Usage:

$database =
new Database();

$service =
new UserService(
$database
);

Visualization

Database

Constructor

UserService

Why Constructor Injection?

Because dependencies become:

✅ Required

✅ Immutable

✅ Easy to understand


Enterprise Example

class AuthService {

public function __construct(

private Database $database,
private Logger $logger,
private Cache $cache

) {

}

}

Method Injection

Dependencies are passed to methods.


Example:

class UserService {

public function save(
Database $database
) {

}

}

Usage:

$service->save(
$database
);

When To Use

Useful when dependency is only needed once.


Property Injection

Example:

class UserService {

public Database $database;

}

Usage:

$service =
new UserService();

$service->database =
new Database();

Why Property Injection Is Bad

Problems:

❌ Dependency may not exist.

❌ Hard to track.

❌ Can become null.


Constructor Injection is usually preferred.


Tight Coupling

Bad Example:

class UserService {

private Database $database;

public function __construct()
{
$this->database =
new Database();
}

}

Loose Coupling

Good Example:

class UserService {

public function __construct(
Database $database
) {

}

}

Why Loose Coupling Matters

Because systems become replaceable.


Example:

MySQL

can become:

MongoDB
Redis
SQLite

without rewriting everything.


Interfaces + DI

This is where DI becomes extremely powerful.


Example:

interface DatabaseInterface {

public function query(
string $sql
);

}

Implementation:

class MysqlDatabase
implements DatabaseInterface {

}

Another:

class MongoDatabase
implements DatabaseInterface {

}

Service:

class UserService {

public function __construct(

DatabaseInterface $database

) {

}

}

Now:

Any database can be injected.

Amazing flexibility.


Visualization

UserService

DatabaseInterface

┌───────────────┐
│ MySQL │
│ MongoDB │
│ SQLite │
└───────────────┘

Dependency Inversion Principle

One of SOLID principles.

Rule:

Depend on abstractions,
not implementations.

Bad:

UserService

MysqlDatabase

Good:

UserService

DatabaseInterface

Service Container

A Service Container is:

A system that automatically
creates dependencies.

Example

Instead of:

$database =
new Database();

$service =
new UserService(
$database
);

Container does it automatically.


Laravel Example

public function __construct(

UserRepository $users

)

Laravel automatically injects:

UserRepository

Magic.


Internal Flow

Controller

Container

Create Dependencies

Inject Dependencies

Example Container

class Container {

private array $services = [];

}

Register:

$container->set(
Database::class,
new Database()
);

Retrieve:

$container->get(
Database::class
);

PocketMine Example

PocketMine plugins often use managers.


Bad:

new RankManager();
new DatabaseManager();
new APIManager();

everywhere.


Good:

class Main {

private RankManager $ranks;

}

Inject managers.


ZyroNetwork Example


Architecture:

Main

├── DatabaseManager
├── RankManager
├── APIManager
├── FormManager
└── NetworkManager

Dependencies are injected.


Example:

class RankManager {

public function __construct(

DatabaseManager $database

) {

}

}

Visualization:

DatabaseManager

RankManager

APIManager

Real Enterprise Architecture

Controller

Service

Repository

Database

Everything uses DI.


Testing Benefits

Suppose:

UserService

depends on:

DatabaseInterface

For testing:

class FakeDatabase
implements DatabaseInterface {

}

Inject fake object.

Testing becomes easy.


Unit Testing Example

$database =
new FakeDatabase();

$service =
new UserService(
$database
);

No real database needed.


Common Beginner Mistakes


Creating Objects Everywhere

Bad:

new Database();
new Logger();
new Cache();

inside every class.


Static Abuse

Bad:

Database::query();

everywhere.


Creates hidden dependencies.


Not Using Interfaces

Interfaces make systems flexible.


Good Example

AuthService

DatabaseInterface

Bad Example

AuthService

MysqlDatabase

Constructor Promotion Example

PHP 8:

class UserService {

public function __construct(

private Database $database,
private Logger $logger

) {

}

}

Very clean.


Dependency Graph

Application

├── Database
├── Logger
├── Cache

└── UserService

Large Enterprise Example

App

├── Controllers
├── Services
├── Repositories
├── Managers
├── Events
├── DTO
└── Contracts

Everything communicates using DI.


Best Practices

✅ Prefer Constructor Injection.

✅ Depend on Interfaces.

✅ Avoid static dependencies.

✅ Use Service Containers.

✅ Keep dependencies small.


Bad Signs

❌ Constructor has 15 dependencies.

Usually means:

Class has too many responsibilities.

Exercises


Exercise 1

Create:

DatabaseInterface

Exercise 2

Create:

MysqlDatabase

Exercise 3

Inject database into:

UserService

Mini Project

Create:

ZyroNetwork Authentication System

Classes:

AuthService
UserRepository
DatabaseManager
LoggerManager
CacheManager

Use Dependency Injection everywhere.


Interview Questions


What is Dependency Injection?

Providing dependencies from outside instead of creating them internally.


Why is DI useful?

Because it creates:

Loose Coupling

What are the three types of DI?

  1. Constructor Injection
  2. Method Injection
  3. Property Injection

Which type is preferred?

Constructor Injection

What principle does DI support?

Dependency Inversion Principle

Why do frameworks use DI?

For scalability, maintainability and testing.


Summary

Dependency Injection provides:

✅ Loose Coupling

✅ Easier Testing

✅ Better Architecture

✅ Flexible Systems

✅ Cleaner Code

✅ Enterprise Scalability

Dependency Injection is one of the foundations of modern PHP frameworks and large projects like:

  • Laravel
  • Symfony
  • PocketMine Plugins
  • ZyroNetwork APIs
  • Enterprise Applications

References

https://phptherightway.com/#dependency_injection

https://martinfowler.com/articles/injection.html

https://laravel.com/docs/container


Next Chapter

➡ SOLID Principles

➡ Design Patterns

➡ Service Containers

➡ Repository Pattern

➡ MVC Architecture