黄麻油 / 容器
简单而强大的依赖注入容器
Requires
- php: >=8.1
- psr/container: ^1.1
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpunit/phpunit: ^9.5
- roave/security-advisories: dev-latest
- vimeo/psalm: ^5.0
README
简单而强大的依赖注入容器
composer require castor/container
基本用法
您可以简单地启动容器实例,所有内容都准备好使用。
您可以使用 Castor\Container::register
方法注册服务工厂(这些工厂只是创建服务的闭包)。
<?php $container = Castor\Container::boot(); $container->register('foo', fn() => new Foo()); $foo = $container->get('foo');
启动默认值甚至不需要进行一些注册。
用例问题
我可以从服务工厂中检索其他服务吗?
是的,您可以。每个服务工厂闭包都接受一个 Psr\Container\ContainerInterface
实例作为唯一参数。
<?php use Psr\Container\ContainerInterface; $container = Castor\Container::boot(); $container->register('foo', static function (ContainerInterface $container) { $bar = $container->get(Bar::class); return new Foo($bar); }); $foo = $container->get('foo');
我可以使用服务提供者吗?
是的,您可以。在 castor/container
中,服务提供者只是一个以 Castor\Container
实例作为唯一参数的可调用。
<?php $container = Castor\Container::boot(); $container->provide(function (Castor\Container $container) { $container->register('foo', fn() => new Foo()); }); $foo = $container->get('foo');
建议这些服务提供者是可调用的类,这样您就可以将其他依赖项传递给它们,例如一个类型配置类。然而,普通的闭包也完全一样。
我可以修改服务吗?
是的,您可以。有两种修改服务的方式。
您可以对从容器中获取的服务进行 inflect。这意味着您可以更改该服务的状态,而不会改变其引用。
Inflectors 是作为接受 inflected 服务和 Psr\Container\ContainerInterface
实例作为参数的闭包进行注册的。
<?php use Psr\Container\ContainerInterface; $container = Castor\Container::boot(); $container->register('foo', fn() => new Foo()); $container->inflect('foo', function (Foo $foo, ContainerInterface $container) { $foo->setBar($container->get(Bar::class)); }); $foo = $container->get('foo');
您还可以对服务进行 decorate。这意味着您可以修改一个服务,并返回另一个引用,只要它符合 Liskov 替代原则。
<?php use Psr\Container\ContainerInterface; $container = Castor\Container::boot(); $container->register('foo', fn() => new Foo()); $container->inflect('foo', function (Foo $foo, ContainerInterface $container) { return new FooBar($foo, $container->get(Bar::class)); }); $foo = $container->get('foo'); // instance of FooBar
如果我修改了一个尚未注册的服务会发生什么?
这不是问题。即使它们尚未由其他提供者或容器本身注册,您也可以为服务注册 inflectors 和 decorators。
如果尚未注册实际的服务工厂,那么即使服务定义中包含 inflectors 或 decorators,该服务在技术上也不存在于容器中。
重要的是要注意,inflectors 和 decorators 是按照它们在容器中注册的顺序调用的。因此,如果您有需要以特定顺序注册 inflectors 或 decorators 的服务提供者,则必须由您以正确的顺序调用它们。这并不是一个与 inflectors 有关的问题,而是一个与 decorators 有关的问题,因为您可能希望以特定的顺序构建对象树。
我可以懒散地使用反射吗?
是的,您可以。Castor\Container::boot
方法接受一个整数作为参数。这个整数是一些标志的组合。所有可用的标志都是 Castor\Container
类的公共常量,并且它们有适当的文档。
有两个容器标志处理反射。
当传递时,Castor\Container::LAZY_BINDING
标志(1)指示容器使用反射解析传递给 Castor\Container::register
方法的类名。您可以通过传递一个具体实现或将抽象绑定到具体实现来实现这一点。
<?php $container = Castor\Container::boot(); // Foo will be instantiated using reflection when a service Foo is requested. $container->register(Foo::class); // Foo will be instantiated when a FooInterface service is requested. $container->register(FooInterface::class, Foo::class);
允许您使用反射的另一个标志是 Castor\Container::EXTRA_LAZY_BINDING
(2)。当传递时,此标志指示容器尝试解析任何请求的服务,即使尚未显式调用 Castor\Container::register
。
<?php $container = Castor\Container::boot(); // Foo will be automatically instantiated using reflection. $container->get(Foo::class);
与使用反射的每个服务容器一样,您的构造函数中需要包含类型信息,以便容器能够确定可以注入哪些服务。当容器无法解析这些服务时,将抛出ContainerError
异常。
我可以禁用反射吗?
可以。您只需使用不包含反射标志的$flag
参数实例化容器即可。
<?php $container = Castor\Container::boot(4); // This flag only enables caching.
我可以为服务指定别名吗?
可以。只需使用服务名称和对应的别名调用Castor\Container::alias
方法即可。
<?php $container = Castor\Container::boot(); $container->register(Foo::class); $container->alias(Foo::class, 'foo');
我可以为服务指定标签吗?
可以。只需使用标签名称和您希望标记的服务调用Castor\Container::tag
方法即可。
<?php $container = Castor\Container::boot(); $container->register(Foo::class); $container->tag('dummy_services', Foo::class, Bar::class);
标签将以数组的形式从容器中获取。
我可以缓存一些服务吗?
是的,默认情况下,所有服务都会被缓存。这意味着一旦调用工厂,您每次调用Castor\Container::get
方法时都会得到相同的实例(引用)。
您可以通过在创建容器时不传递Castor\Container::CACHE_MODE
(4)来禁用此行为。
目前,无法按服务配置缓存。