gravatalonga/container

Implementation of PSR-11 Container, it is lightweight yet powerful. Feature such as Lazy Factory, Factory, Optinal Share of Services, Array Access and other it can be very versatile.

Fund package maintenance!
gravataLonga

1.8.4 2022-06-07 08:00 UTC

README

Container

Container

Latest Version on Packagist Software License Total Downloads Coverage Status Quality Score Type coverage PHP Unit Say Thanks

Container implementation which follow PSR-11.

Requirements

PHP VERSION Container Version
8.0 < <= 1.6 1.0
>= 8.0 >= 1.7 1.0
>= 8.0 >= 1.8 2.0

Installation

composer require gravatalonga/container

Usage

Basic Usage

use Gravatalonga\Container\Container;

$container = new Container();
$container->set('random', function() {
    return rand(0, 1000);
});

$container->share('uniqueSeed', function() {
    return rand(0, 1000);
});

// create alias 'sessionId'  
$container->alias('uniqueSeed', 'sessionId');

echo $container->get('random'); // get random number each time you call this.

if ($container->has('uniqueSeed'))  {
    echo $container->get('uniqueSeed'); // get same random number.  
}

When creating a new instance of Container, you can pass on first argument configurations or entries to be already bonded into container.

use Gravatalonga\Container\Container;

new Container([
    'settings' => ['my-settings'], 
    FooBar::class => function (\Psr\Container\ContainerInterface $c) { 
        return new FooBar();
    }
]);

Using Service Provider

use Gravatalonga\Container\Container;


$container = new Container();
$container->set(RedisClass::class, function () {
    return new RedisClass();
});

// then you can use...
$cache = $container->get('Cache');

When using set, factory or share with Closure and if you want to get Container it's self, you can pass type hint of ContainerInterface as argument.

use Gravatalonga\Container\Container;
use Psr\Container\ContainerInterface;

$container = new Container([
    'server' => 'localhost',
    'username' => 'root'
]);

$container->set('Cache', function (ContainerInterface $container) {
    // some where you have binding this RedisClass into container... 
    return $container->make(RedisClass::class, [
        'server' => $container->get('server'), 
        'username' => $container->get('username')
    ]);
});

// set is a method alias of factory
$container->factory('CacheManager', function() {
    return new CacheManager();
});

// then you can use...
$cache = $container->get('Cache');

Using Array like access

use Gravatalonga\Container\Container;

$container = new Container();  
$container[FooBar::class] = function(ContainerInterface $container) {
    return new FooBar($container->get('settings'));
};

if (isset($container[FooBar::class])) {
    echo $container[FooBar::class]->helloWorld();
}

Alias

Alias like the name show, it to make a possibility to make an alias from one entry to another. It will throw an exception if can't be found.

use Gravatalonga\Container\Container;

$container = new Container();  
$container->set('settings', ['driver' => 'default']);
$container->set(FooBar::class, function($settings) {
    return new FooBar($settings);
});

$container->alias(FooBar::class, 'foo.bar');

$foobar = $container->get('foo.bar');

Callable as alternative

use Gravatalonga\Container\Container;

$class = new class
{
    public function get(): int
    {
        return mt_rand(0, 100);
    }
};

$container = new Container();  
$container->factory('random', [$class, 'get']);

$foobar = $container->get('random'); // it will get random int

Extend

In order implementation to be ease for other services providers extend method was created.

use Gravatalonga\Container\Container;

class Test
{
    /**
     * @var string
     */
    public $name;

    public function __construct($name = 'Jonathan Fontes')
    {
        $this->name = $name;
    }
}

$container = new Container(); 

$container->get(Test::class, function(ContainerInterface $container) {
    return new Test;
});

$container->extend(Test::class, function(ContainerInterface $container, $test) {
    return new Test($test->name.' - The greatest!');
});

echo $container->get(Test::class); // It will print 'Jonathan Fontes - The greatest!';  

Advance usage

Container is capable to resolve class who isn't bounded, it will resolve dependencies from __construct type-hint/built-in which is bounded. Read example code below:

Information: built-in is type which is built in on PHP, which is string, int, boolean, etc. Type Hint is type which is created by user land, such as, when creating a class you are creating a new type.

Using Type Hint Class

use Gravatalonga\Container\Container;

class FooBar {}

class Test
{
    public function __construct(FooBar $foobar)
    {
        $this->foobar = $foobar;
    }
}

$container = new Container(); 
$container->set(FooBar::class, function () {
    return new FooBar();
});

$container->get(Test::class); // FooBar it will inject into Test class.  

Note: We only support resolving auto wiring argument on construction if they are bounded into container. Otherwise it will throw an exception if can't find entry.

Using Built in type

use Gravatalonga\Container\Container;
class Test
{
    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

$container = new Container(); 
$container->set('name', 'my-var');

$container->get(Test::class); // my-var it will inject into Test class.  

If argument accept nullable if can't be resolve it will pass default value which in this case is null.

use Gravatalonga\Container\Container;

class Test
{
    /**
     * @var string
     */
    private $name;

    public function __construct(string $name = null)
    {
        $this->name = $name;
    }
}

$container = new Container(); 

$container->get(Test::class); // null it will inject into Test class.  

In also attempt to resolve auto wiring of construction by its default value, it will check default value of __construct and it will pass that default value.

First case, if value is a simple built-in type value.

use Gravatalonga\Container\Container;

class Test
{
    /**
     * @var string
     */
    private $name;

    public function __construct($name = 'Jonathan Fontes')
    {
        $this->name = $name;
    }
}

$container = new Container(); 

$container->get(Test::class); // 'Jonathan Fontes' it will pass into container...

Tip

It's well-known that using singleton pattern, it's an anti-pattern. But small feature can't hurt you

So, you can use:


$container = new Container();

// ... 

Container::setInstance($container);

Then you can get instance of container,

$container = Container::getInstance();

Tips: The container can detected circular dependencies.

Change log

Please see CHANGELOG for more information on what has changed recently.

Testing

composer grumphp

Contributing

Please see CONTRIBUTING and CODE_OF_CONDUCT for details.

Security

If you discover any security related issues, please email jonathan.alexey16[at]gmail.com instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.