hartmann / planck
极简主义,PSR-11(+) 兼容,基于提供者的容器
Requires
- php: >=7.1
- container-interop/service-provider: ^0.4.0
- hartmann/resolve-strategy-interface: ^1.0
- psr/container: ^1.0
Requires (Dev)
- phpunit/phpunit: ^7.5
README
Planck
Planck 是一个极简主义的依赖注入容器,具有 PSR-11+ 支持,灵感来源于 Pimple/Simplex。目前,我甚至使用了它们的大部分文档,但以后我会做出改变。
-
Hartmann\Planck\Container实现ContainerInterface并完全支持 container-interop 的ServiceProviderInterface$container->extend()- 可以用来扩展标量值、工厂和服务
$container->factory()- 可以用来标记一个可调用对象为工厂服务。如果是这样,每次请求条目时都会返回一个新的实例
$container->preserve()- 可以用来保护/保留一个函数不被容器用作服务工厂
$container->autowire()- 可以用来自动连接函数和类
安装
composer require hartmann/planck
使用
创建容器只需创建一个 Container 实例
$container = new \Hartmann\Planck\Container();
定义服务提供者
服务提供者是一个对象,它作为更大系统的一部分执行某些操作。服务示例:数据库连接、模板引擎或邮件发送程序。几乎任何 全局 对象都可以是服务。
服务通过返回对象实例的 匿名函数 定义
use Interop\Container\ServiceProviderInterface class Provider implements ServiceProviderInterface { public function getFactories() { return [ stdClass::class => function(ContainerInterface $container) { return new stdClass; }, ... ]; } public function getExtensions() { return [ stdClass::class => function(ContainerInterface $container, ?stdClass $class) { $class->foo = 'bar'; return $class; }, ... ]; } }
注意,匿名函数可以访问当前容器实例,允许引用其他服务或参数。
由于对象仅在获取时创建,因此定义的顺序无关紧要。
使用定义的服务也非常简单
$class = $container->get(stdClass::class);
定义工厂服务
默认情况下,每次获取服务时,Planck 都返回其 相同实例。如果您希望所有调用都返回不同的实例,请使用 factory() 方法包装匿名函数
$container->set('factory', $container->factory(function (ContainerInterface $container) { return new stdClass; }));
现在每次调用 $container->get(stdClass::class) 都会返回一个新的 stdClass 实例。
定义参数
定义参数可以简化容器的外部配置并存储全局值
$container->set('cookie_name', 'SESSION_ID'); $container->set('session_storage_class', 'SessionStorage');
您现在可以轻松地通过覆盖 session_storage_class 参数来更改 cookie 名称,而不是重新定义服务定义。
保留/保护参数
由于 Planck 将匿名函数视为服务定义,您需要使用 preserve() 方法将它们包装起来以将它们存储为参数
$container->set('random_bytes', $container->preserve(function () { return random_bytes(4); }));
定义后修改服务
在某些情况下,您可能希望在定义后修改服务定义。您可以使用 extend() 方法定义在服务创建后运行的附加代码
$container->set('session_storage', function (ContainerInterface $container) { return new $container->get('session_storage_class')($container->get('cookie_name')); }); $container->extend('session_storage', function (ContainerInterface $container, ?SessionStorage $storage) { $storage->...(); return $storage; });
自动连接
有时在容器本身上解决依赖项是有用的。为此,可以使用 autowire() 方法。
类和匿名函数都可以连接。
$container->set(Foo::class, new Foo()); $container->set(Bar::class, new Bar()); $container->set('autowired', $container->autowire(function (Foo $foo, Bar $bar) { return ... }));
autowire() 方法有两个参数 array $parameters = []。
如果您知道容器无法解析参数或您希望传递自己的值,您可以轻松地做到这一点
class Foo { ... } $container->set(Foo::class, new Foo()); $container->set('autowired', $container->autowire(function (Foo $foo, $bar) { var_dump($foo) // object Foo var_dump($bar) // string 'foo' }, ['bar' => 'foo']]));
自版本 1.0.3 开始,可以将可调用对象以数组形式传递。
这允许自动注入静态和非静态对象方法,这在许多方面都非常有用,例如控制器。
class HomeController { protected $logger public function __contruct(LoggerInterface $logger) { $this->logger = $logger; } public function index(Request $request, Response $response): Response { $this->logger->info('someone visited my site!'); return $response->write('Hello'); } } // adding the required classes to the container ... $container->autowire([HomeController::class, 'index']);
这也适用于已经实例化的对象。
$container->autowire([$homeControllerInstance, 'index']);
只要依赖项已在容器中注册,扩展类就会按正常方式表现。
class Request { public function __construct(string $method, UriInterface $uri, HeadersInterface $headers, ...); } class CreateUserRequest extends Request { ... } // adding the required classes to the container ... $container->autowire(CreateUserRequest::class, ['method' => $requestMethod]);
提示参数 & 自动注入
PHP 5 和 7 中添加了命名参数。Planck 可以处理内置和常规提示。
以下是一些可能的配置。
// unresolvable, must be passed directly to the parameters function ($foo) { } // unresolvable, must be passed directly to the parameters function (string|int|float|array|bool $foo) { } // hinted, required function (Foo $foo) { } // hinted, optional function (Foo $foo = null) { } // hinted, nullable function (?Foo $foo) { }
如果找不到可空参数的值,则传递 null。
如果找不到可选参数的值,则传递默认值。
不支持引用参数,您必须使用 set 方法注册此类条目。
隐式自动注入
Planck 还提供了隐式自动注入的选项,即尚未存储在容器中的类请求可以自动创建。
要激活此功能,必须调用以下方法
$container->enableImplicitAutowiring(true); // enable $container->enableImplicitAutowiring(false); // disable
现在可以无错误地调用以下内容
$container = new \Hartmann\Planck\Container $container->enableImplicitAutowiring(true); $container->set('autowired', $container->autowire(function(Foo $foo, Bar $bar) { return ... })); $value = $container->get('autowired');
在类被隐式加载后,它将直接存储在容器中。
只能隐式加载类。
解析策略
可以使用解析策略自动解析可以类似创建的类。
例如,如果您使用 FormRequests 来验证输入字段,它们可以使用相应的策略进行解析,而无需为每个创建服务工厂。
这可能看起来像这样
use \Hartmann\ResolveStrategy\ResolveStrategyInterface class RequestResolveStrategy implements ResolveStrategyInterface { public function suitable(string $class): bool { return method_exists($class, 'createFromEnvironment') && in_array(FormRequest::class, class_parents($class)); } public function resolve(\Psr\Container\ContainerInterface $container, string $class) { return call_user_func([$class, 'createFromEnvironment'], $container->get('environment')); } } $container = new \Hartmann\Planck\Container(); $container->enableImplicitAutowiring(true); $container->addResolveStrategy(new RequestResolveStrategy()); $container->get(CreateUserFormRequest::class); $container->get(DeletePostFormRequest::class); $container->get(LoginFormRequest::class);
要使此功能正常工作,必须启用隐式自动注入。