ecfectus / container
轻量级PSR11兼容依赖注入容器
Requires
- php: >=7.0.0
- container-interop/container-interop: ^1.1
Requires (Dev)
- phpdocumentor/phpdocumentor: 2.*
- phpunit/phpunit: ^5.5.0
Provides
This package is not auto-updated.
Last update: 2024-09-18 20:38:45 UTC
README
轻量级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 container(比 illuminate 快)之间进行了一个非常原始的基准测试。
看起来很积极。大约快20%,并且通常使用更少的内存(高达70%更少)。
反射容器给您完全的自由,但这需要付出代价(可能是2+倍慢),尽可能提供依赖列表并将所有对象注册到容器中,以确保最佳性能。