/di

A PSR-11 compliant Dependency Injector component

Primary LanguagePHPMIT LicenseMIT

Omar Dependency Injector

Packagist Build Status Coverage Status StyleCI Mutation testing badge Psalm type coverage

Reliability Rating Maintainability Rating Quality Gate Status

A PSR-11 compliant Dependency Injector component

Installation

With composer:

composer require omarphp/di

Usage

Building a simple class

You can instantiate a class without a constructor parameter with zero configuration.

use Omar\DependencyInjection\Container;

class RockBand {}

$container = Container::create();

$band = $container->get(RockBand::class);
assert($band instanceof RockBand);

Autowiring

OmarDI can autowire parameter constructors.

use Omar\DependencyInjection\Container;

class Guitarist {}

class Drummer {}

class RockBand
{
    public function __construct(Guitarist $guitarist, Drummer $drummer) {}
}

$container = Container::create();

$band = $container->get(RockBand::class);
assert($band instanceof RockBand);

Binding

You can bind implementations to abstract classes and interfaces with configuration.

use Omar\DependencyInjection\Config;
use Omar\DependencyInjection\Container;

interface Guitarist {}
abstract class Drummer {}

class JimmyPage implements Guitarist {}
class JohnBonham extends Drummer {}

$config = Config::init()
    ->bind(Guitarist::class, JimmyPage::class)
    ->bind(Drummer::class, JohnBonham::class);

$container = Container::create($config);

$guitarist = $container->get(Guitarist::class);
$drummer = $container->get(Drummer::class);

assert($guitarist instanceof JimmyPage);
assert($drummer instanceof JohnBonham);

Autowiring can use the bound parameters.

use Omar\DependencyInjection\Config;
use Omar\DependencyInjection\Container;

interface Guitarist {}
abstract class Drummer {}

class JimmyPage implements Guitarist {}
class JohnBonham extends Drummer {}

class RockBand
{
    public function __construct(Guitarist $guitarist, Drummer $drummer) {}
}

$config = Config::init()
    ->bind(Guitarist::class, JimmyPage::class)
    ->bind(Drummer::class, JohnBonham::class);

$container = Container::create($config);

$band = $container->get(RockBand::class);
assert($band instanceof RockBand);

Setup constructor parameters

Wire implementations to constructor parameter

You can wire the constructor parameters by their name. That's how you can inject different implementations for the same interfaces.

use Omar\DependencyInjection\Config;
use Omar\DependencyInjection\Container;
use Omar\DependencyInjection\Setup;

interface Guitarist {}
interface Drummer {}

class JimmyPage implements Guitarist {}
class George implements Guitarist {}
class JohnBonham implements Drummer {}
class Ringo implements Drummer {}

abstract class RockBand
{
    public function __construct(Guitarist $guitarist, Drummer $drummer) {}
}

class LedZeppelin extends RockBand {}
class Beatles extends RockBand {}

$config = Config::init()
    ->setup(LedZeppelin::class, Config::params()
        ->wire('guitarist', JimmyPage::class)
        ->wire('drummer', JohnBonham::class)
    )
    ->setup(Beatles::class, Config::params()
        ->wire('guitarist', George::class)
        ->wire('drummer', Ringo::class)
    );

$container = Container::create($config);

$ledzep = $container->get(LedZeppelin::class);
assert($ledzep instanceof LedZeppelin);

$beatles = $container->get(Beatles::class);
assert($beatles instanceof Beatles);

Configure values to constructor parameters

Scalar values or object instances can be configured to constructor parameters by their name.

use Omar\DependencyInjection\Config;
use Omar\DependencyInjection\Container;
use Omar\DependencyInjection\Setup;

class Guitarist
{
    public function __construct(string $guitarType, int $bornInYear) {}
}

$config = Config::init()
    ->setup(Guitarist::class, Config::params()
        ->config('guitarType', 'Les Paul')
        ->config('bornInYear', 1944)
    );

$container = Container::create($config);

$jimmyPage = $container->get(Guitarist::class);
assert($jimmyPage instanceof Guitarist);

Configured and wired parameters

They can be used together.

use Omar\DependencyInjection\Config;
use Omar\DependencyInjection\Container;
use Omar\DependencyInjection\Setup;

interface Guitar {}
class LesPaul implements Guitar {}

class Guitarist
{
    public function __construct(Guitar $guitar, int $bornInYear) {}
}

$config = Config::init()
    ->setup(Guitarist::class, Config::params()
        ->wire('guitar', LesPaul::class)
        ->config('bornInYear', 1944)
    );

$container = Container::create($config);

$jimmyPage = $container->get(Guitarist::class);
assert($jimmyPage instanceof Guitarist);

Provider callback functions

You can set up your instances to be created with a callback function. These functions can also get their parameters from the container.

use Omar\DependencyInjection\Config;
use Omar\DependencyInjection\Container;

interface Guitar {}
class LesPaul implements Guitar {}

class Guitarist
{
    public function __construct(Guitar $guitar, int $bornInYear) {}
}

$config = Config::init()
    ->bind(Guitar::class, LesPaul::class)
    ->provider(Guitarist::class, function (Guitar $guitar) {
        return new Guitarist($guitar, 1944);
    });

$container = Container::create($config);

$jimmyPage = $container->get(Guitarist::class);
assert($jimmyPage instanceof Guitarist);

Factory classes

You can create your instances with factories. These factories can be configured with the parameters in the constructor and in the __invoke() method.

use Omar\DependencyInjection\Config;
use Omar\DependencyInjection\Container;

interface Guitar {}
class LesPaul implements Guitar {}

class Guitarist
{
    public function __construct(Guitar $guitar) {}
}

interface Drummer {}
class JohnBonham implements Drummer {}

class RockBand
{
    public function __construct(Guitarist $guitarist, Drummer $drummer) {}
}

class RockBandFactory
{
    /** @var Drummer */
    private $drummer;

    public function __construct(Drummer $drummer) {
        $this->drummer = $drummer;
    }

    public function __invoke(Guitar $guitar): RockBand
    {
        return new RockBand(new Guitarist($guitar), $this->drummer);
    }
}

$config = Config::init()
    ->bind(Drummer::class, JohnBonham::class)
    ->bind(Guitar::class, LesPaul::class)
    ->factory(RockBand::class, RockBandFactory::class);

$container = Container::create($config);

$band = $container->get(RockBand::class);
assert($band instanceof RockBand);

Contributing

  • Pull requests are welcome.
    • For major changes, please open an issue first to discuss what you would like to change.

License

MIT