Guide to Festi DI

Festi DI is Dependency Injection library.

Setup

Composer installation:

"repositories": [
    { "type": "composer", "url": "https://packages.festi.io/" }
]
"require": {
    "festi-team/festi-framework-di": "dev-master",
    "php": ">=8.0"
}

Basic Dependency Injection

Consider the class:

use core\di\Inject;

class Communication 
{   
    #[Inject] 
    private ICommunicator $communicator;

    public function sendMessage(string $message): void
    {
        $this->communicator->sendMessage($message);
    }

}

The basic entry point:

$injector = new core\di\Injector(new BasicModule());
$instance = $injector->getInstance(Communication::class);

The Module is the basic unit of definition of bindings (Container).

Bindings

With bindings, you define how Festi DI is going to inject dependencies into a class. A binding is defined in an implementation of core\di\AbstractModule:

class BasicModule extends core\di\AbstractModule
{
    public function configure()
    {
        $this->bindByClassName(ICommunicator::class, CommunicatorImpl::class);
    }
}

This module implementation specifies that an instance of CommunicatorImpl is to be injected wherever a ICommunicator is found with attribute Inject.

Another incarnation of this mechanism is the named binding:

#[Inject(name:'communication')] 
private ICommunicator $communicator;
class BasicModule extends core\di\AbstractModule
{
    public function configure()
    {
        $this->bindByName('communication', CommunicatorImpl::class);
    }
}

Another approach to constructor-specific binding is the instance binding, where we provide an instance directly in the binding:

class BasicModule extends core\di\AbstractModule
{
    public function configure()
    {
        $this->bindByInstance(ICommunicator::class, new CommunicatorImpl());
    }
}

Types of Dependency Injection

Field Injection

#[Inject]
public InterfaceA $a;

#[Inject(ClassA::class)]
protected $b;

#[Inject]
private InterfaceA $c;

#[Inject(name: 'setting_key')]
public string $d;

#[Inject]
public ClassA $f;

Method Injection

Here we use a setter method to achieve the injection:

#[Inject]
public function setA(IMethod $method): void
{
    $this->methodA = $method;
}

#[Inject]
protected function setB(#[Inject(IMethod::class)] $method): void
{
    $this->methodB = $method;
}

#[Inject]
protected function setC(#[Inject(name: 'setting_key')] string $value): void
{
    $this->methodC = $value;
}

Constructor Injection

You can also inject dependencies using a constructor:

public function __construct(
    #[Inject] public IConstructItem $a,
    #[Inject(IConstructItem::class)] $b,
    #[Inject(name: 'setting_key')] $c,
    #[Inject] ClassConstructB $d
)
{

}