/container

A very simple, PSR-11 compatible container

Primary LanguagePHPMIT LicenseMIT

container

Build Status codecov Software License

A simple, small PSR-11 compliant container for PHP 7+. It has the following features:

  • Factory and shared definitions
  • Support for non object services (ie: storing key values)
  • Aliases
  • Autowiring

Installation

The easiest way to install is via composer:

composer install ronanchilvers/container

Usage

Basic usage is simple:

$container = new Container;
$container->set('my_service', function () {
    return new \My\Service();
});

$myService = $container->get('my_service');

By default services added to the container are factory services - you'll get a new one every time. If you want to define a shared service you can do:

$container = new Container;
$container->share('my_shared_service', function () {
    return new \My\Service();
});

$sharedService = $container->get('my_shared_service');

You can also register primitives with the container:

$container = new Container;
$container->set('settings', [
    'db' => [
        'adaptor'  => 'mysql',
        'username' => 'foobar',
        'password' => 'supersecret',
        'hostname' => '127.0.0.1'
    ]
]);
$container->set('my_string', 'foobar');

$settings = $container->get('settings');
$settings = $container->get('my_string');

Aliases

Sometimes its useful to be able to alias a service. For example if you want to register a service with a simple string name but also refer to it by an interface name. To do this you can use a Symfony style prefix on the definition to indicate that its a reference to another service.

Here's an example:

$container = new Container;
$container->share('logger', function (){
    return new PSR11Logger();
});
$container->set('Psr\Log\LoggerInterface', '@logger');

// This:
$logger = $container->get('logger');
// returns the same instance as this:
$logger = $container->get('Psr\Log\LoggerInterface');

Extending services

The container allows services to be extended (just like Pimple) using the extend() method. You can extend both factory and shared services. Call extend() with the service id and a callable. The callable will recieve the service instance as its first argument and the container as the second. You can extend a service as many times as you like.

$container = new Container;
$container->share('my_service', function () {
    return new \My\Service;
});
$container->extend('my_service', function ($s, $c) {
    $s->registerWidget(new Widget);

    return $s;
});

$service = $container->get('my_service');

Autowiring

The container supports basic autowiring. This means that you can supply a fully qualified class name as a service definition and the container will attempt to instantiate it for you.

$container = new Container;
$container->set('logger', '\App\MyLogger');

$logger = $container->get('logger');

Constructor injection is also supported for type hinted parameters.

use Psr\Log\LoggerInterface;

class MyLogger implements LoggerInterface
{
    ...
}
class MyService
{
    public function __construct(LoggerInterface $logger)
    {
        ...
    }
}
$container = new Container;
$container->share(LoggerInterface::class, 'MyLogger');
$container->share(MyService::class, 'MyService');

// This will return an instantiated service with the logger injected
$service = $container->get(MyService::class);

The injected objects do not have to be registered with the container to be injected. If the container encounters a dependency that is not defined as a service it will attempt to create a new instance with no constructor parameters.

Service Providers

The container supports pimple style service providers. Your provider must implement Ronanchilvers\Container\ServiceProviderInterface.

class ServiceProvider implements ServiceProviderInterface
{
    /**
     * @author Ronan Chilvers <ronan@d3r.com>
     */
    public function register(Container $container)
    {
        $container->set('my_service', function () {
            return new StdClass;
        });
    }
}

$container = new Container;
$container->register(new ServiceProvider);

$myService = $container->get('my_service');

Testing

The container is quite simple and has 100% test coverage. You can run the tests by doing:

./vendor/bin/phpunit

The default phpunit.xml.dist file creates coverage information in a build/coverage subdirectory.

Contributing

If anyone has any patches they want to contribute I'd be more than happy to review them. Please raise a PR. You should:

  • Follow PSR2
  • Maintain 100% test coverage or give the reasons why you aren't
  • Follow a one feature per pull request rule

License

This software is licensed under the MIT license. Please see the License File for more information.