一致性/容器

轻量级PSR11兼容依赖注入容器

dev-master 2016-10-01 20:48 UTC

This package is not auto-updated.

Last update: 2024-09-14 18:04:20 UTC


README

Build Status

轻量级PSR11(草案)兼容依赖注入容器。

受到 illuminate/container 和 league/container 的启发,此容器具有一些优点。

低内存占用

这是一个非常懒惰的容器,将服务绑定到其中只是将其存储在其内部定义(这是一个数组,而不是对象)中。

不会创建对象,不会实例化参数,它只是存储回调。

别名回调仅在通过 ::get($alias) 方法请求别名时调用。

无反射的回调注入

illuminate/container 将整个容器实例提供给回调,而 league/container 返回一个定义,您可以在其中以流畅的方式添加参数。

问题在于,您可能正在向可能不需要太多内容的回调传递整个容器对象,消耗内存。

或者您正在创建额外的对象来存储定义,再次消耗内存。

解决方案受 laravels 路由组件的启发,提供数组作为回调

//this is just registering a normal callback which can optionally accept the container instance.
$container->bind(SomeClassInterface::class, function($container){//optionally get the passed in container, this is default
    return new SomeImplementation();
});


//this method allows you to define the whole dependency list for the callback
//by passing an array with the callback as the first item, and what needs to be passed to it as the rest of the array
//this allows for saving in both speed and memory usage.
$container->bind(SomeClassInterface::class, [function(A $a, B $b){
    return new SomeImplementation();
}, A::class, B::class]);

我们发现,即使测试只有 5 个依赖类,这种方法提供依赖列表的速度也快两倍,并且不需要反射。

用法

该容器符合当前的PSR11容器规范(草案),因此基本用法如下

$container = new Ecfectus\Container\Container();
//optionally include a reflection based delegate (only used if no registration exists, this is required if you plan on using the service provider container as well)
$container->delegate(new Ecfectus\Container\ReflectionContainer());

//optionally include the service provider container
$serviceProviderContainer = new ServiceProviderContainer();
$container->delegate($serviceProviderContainer);

//if registered add service providers either on the delegated container OR on the main container via the use of magic methods
$serviceProviderContainer->addServiceProvider(\Name\Of\ServiceProvider::class);//must extend the \Ecfectus\Container\ServiceProvider\AbstractServiceProvider class

//psr interface methods

$container->get('id');//returns the instance, or throws a NotFoundException

$container->has('id');//returns bool

//methods specific to this container

//bind a service
$container->bind($id, $callbackOrArray, $share = false);

//share a service (same as above with third flag as true)
$container->share($id, $callbackOrArray);

//$callbackOrArray can be any of the following:

//classname
$container->bind($id, \Some\Class::class);

//instance
$container->bind($id, new \Some\Class());

//callable
$container->bind($id, function($container){
    return new \Some\Class();
});

//an array containing a callable and the required arguments, to save on using reflection (better performance)
$container->bind($id, [function($arg, \Some\Class $instance){
    return $instance->with($arg);
}, 'argument value', \Some\Class::class]);

//allows you to alter the instance before returning it from the container, you must return the instance
$container->extend($id, function($instance, $container){
    return $instance;
});

如果使用服务提供者,它们只需要提供一个它们定义的服务数组,以及创建绑定的注册函数。

<?php
use Ecfectus\Container\ServiceProvider\AbstractServiceProvider;

class SomeServiceProvider extends AbstractServiceProvider
{

    protected $provides = [
      someClass::class
    ];

    public function register(){
            /**
             * Or use the shortcuts $this->bind() and $this->share()
             */
        $this->getContainer()->bind(someClass::class, function(){
            return new someClass();
        });
    }

}

服务提供者还可以实现 BootableServiceProviderInterface 并提供 ::boot() 方法,当服务提供者通过 addServiceProvider 函数添加时,如果容器已经启动,或者当调用 $container->bootServiceProviders(); 方法时,该方法将被调用。此函数也有权访问容器。

注意

在 league 容器(比 illuminate 更快)和此容器之间进行了非常原始的基准测试。

看起来很积极。大约快 20%,并且通常使用更少的内存(多达 70% 更少)。

反射容器给您完全的自由,但这是有代价的(可能是 2+ 倍慢),尽可能提供依赖列表,并将所有对象注册到容器中以确保最佳性能。