Vladimir Kovpak

@cn007b

Namespace Dependency Injection in PHP

Pretty sure that you’ve already familiar with Dependency Injection (DI) design pattern and with types of dependency injections: constructor, setter, interface. Also in some sources, you can find parameter dependency injection type.
In this topic you’ll find yet another type of DI — namespace!

Prerequisites

I expect you already familiar with idea behind DI and you have clear understanding why do you need this. If not — please read basic information about DI first.

Let’s assume that our application is Ukrainian 🇺🇦 VISA center which can provide, approve or decline VISA to any country in the world, for simplicity will consider only the European Union here. First of all, we need interface,
file src/Domain/Interfaces/VISA/EuropeanUnion.php:

Now we have implementation for non-visa-free case (let’s call this case “review”), file src/Infrastructure/VISA/Review/EuropeanUnion.php:

And another one implementation for VISA-FREE case,
file src/Infrastructure/VISA/Free/EuropeanUnion.php:

Target

The main aim — is to have somewhere (for example in file index.php)
super simple code like this:

Solution

Use namespace Dependency Injection and configure your composer.json file:

and run: php composer dump-autoload (it’s important, please don’t forget run this command).

Test

Now just run command: php index.php and you’ll see:

'You have to provide documents for review.'

Now please update composer.json with next content:

and run: php composer dump-autoload and run: php index.php you’ll see:

'Your request is approved!'

That’s it! Congrats!

Comparison with other approaches

Alternatives: Factory Method (or even Abstract Factory), constructor or setter Dependency Injection or Strategy designs patterns. Yes, these patterns can be considered as an alternative, but all of them require you to write technical code like:

class Factory
{
public static function create($name)
{
if (class_exists($name)) {
return new $name();
}
}
}

or

public static function getInstance(string $name)
{
if (!isset(self::$instances[$name])) {
$className = "App\\Infrastructure\\" . ucfirst($name);
if (!class_exists($className)) {
throw new RuntimeException("Unknown class: $className");
}
self::$instances[$name] = new $className();
}
return self::$instances[$name];
}

also in the real-world project, it’ll look more sophisticated, maybe like this. Also you’ll mix your declarative style code with something like:

$name = ($parameter === 'free') ? 'VISA/Free' : 'VISA/Review';
$visa = Factory::create($name);
// and only now
$visa->approve();

And also in case of using these patterns you have to have all code in one place (one repo), but imagine that VISA/Review and VISA/Free may be huge subsystems with own factories, strategies, microservices, internal business logic implementations etc — it may turn out to be nightmare.

With Namespace Dependency Injection you can create dedicated packages (repos) for interfaces, VISA/Review, VISA/Free etc. Every package only has to know about interfaces, no more. So it’s more flexible, easier to maintain, extend and re-use!
You can even have separated development teams for every package, if you have really huge project.

And most important — on application level you‘ll have super simple code in declarative style and all internal details won’t confuse you. Your project will be more predictable because behavior won’t vary at runtime depending on different circumstances, it’ll work strictly accordingly to your configuration.
And also as a bonus, IDE (like PHPStorm) will help you more, because it won’t scream on code like: new $className();.

Performance comparison

Namespace DI is faster that all other types of dependency injections!!! Because this approach has no yaml configs or infrastructural stuff mentioned earlier or DI Container or something else…, hence in this way PHP won’t waste time executing redundant code during each http request!

Why this approach not popular

  1. Because this type of dependency injection possible only in PHP due to the way how autoloading works. It’s impossible to do the same event in JavaScript, no use to speak about other languages. As result — this technic not wide spread.
  2. With other types of dependency injections it’s possible to change application behavior at runtime, but with Namespace DI — it’s impossible.

Conclusion

In case you don’t need to change behavior (or internal implementation) of your application at runtime but need to have opportunity to change it from config file (even composer.json), plus reduce execution time— you can consider as an option Namespace Dependency Injection!

More by Vladimir Kovpak

Topics of interest

More Related Stories