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