3.2. Use the container
使用容器
本文档描述了容器对象本身的API。
get() & has()
容器实现了 PSR-11 标准。 这意味着它实现了 Psr\Container\ContainerInterface
:
namespace Psr\Container;
interface ContainerInterface
{
public function get($id);
public function has($id);
}
我们建议您对接口而不是实现(DI \ Container
)进行类型提示。这样做您的代码就会与与PHP-DI分离,并且您可以随时切换到另一个容器。
set()
您可以直接在容器上设置条目:
$container->set('foo', 'bar');
$container->set('MyInterface', \DI\create('MyClass'));
// 如果需要将闭包设置为原始值,请使用\DI\value,
// 因为默认情况下将闭包解释为工厂
$container->set('myClosure', \DI\value(function() { /* ... */ }));
但还是建议使用定义文件,请参阅 定义文档.
make()
容器还提供了 make()
方法。此方法定义在 DI\FactoryInterface
中。
class GithubProfile
{
public function __construct(ApiClient $client, $user)
...
}
$container->make('GithubProfile', [
'user' => 'torvalds',
]);
make()
方法的工作方式与 get()
相似, 只是 每次调用该条目时都会根据条目的类型解析该条目。 这意味着:
- 如果条目是对象,则每次都会创建一个新实例
- 如果条目是工厂,则每次都会调用工厂
- 如果条目是别名,则别名每次都会解析
请注意,每次只会解析您调用的条目:条目的所有依赖项都不会解析!这意味着,如果该条目是别名,则别名指向的条目将只解析一次。
如果你在 Container::make()
的第二个参数中提供参数, 并且要解析的条目是要创建的对象,则提供的参数将用于对象的构造函数,而缺少的参数参数将从容器中解析。
Container::make()
对于创建不应存储在容器 内部 (即不是服务或非无状态)但具有依赖关系的对象很有用,如果要覆盖对象构造函数的某些参数,它也很有用。
如果你想在服务、控制器或任何地方中使用 make()
方法,建议你对 FactoryInterface
做类型提示。 这样可以避免将代码耦合到容器。 DI\FactoryInterface
会自动绑定到 DI\Container
, 因此你不需要在配置中注入它。
call()
容器暴露了 call()
方法, 它可以让你调用任何PHP可调用的方法。
与 call_user_func()
相比,它提供了以下附加功能:
-
命名参数 (通过名称而不是位置索引来传递参数)
$container->call(function ($foo, $bar) { // ... }, [ 'foo' => 'Hello', 'bar' => 'World', ]); // 例如在微框架中也很有用 $container->call($controller, $_GET + $_POST);
-
基于类型提示的依赖注入
$container->call(function (Logger $logger, EntityManager $em) { // ... });
-
基于显式定义的依赖注入
$container->call(function ($dbHost) { // ... }, [ // 通过参数名称索引 'dbHost' => \DI\get('db.host'), ]); $container->call(function ($dbHost) { // ... }, [ // 或未建立索引 \DI\get('db.host'), ]);
您最好是可以混合使用:
$container->call(function (Logger $logger, $dbHost, $operation) {
// ...
}, [
'operation' => 'delete',
'dbHost' => \DI\get('db.host'),
]);
call()
方法对于调用控制器特别有用,例如:
$controller = function ($name, EntityManager $em) {
// ...
}
$container->call($controller, $_GET); // $_GET 包含 ['name' => 'John']
这让开发人员可以自由编写控制器并使用依赖注入来获取请求参数和服务
与make()
一样, call()
定义在 Invoker\InvokerInterface
(参阅 PHP-DI/Invoker package) 中,这样您就可以针对该接口键入提示,而无需将自己与容器耦合。 Invoker\InvokerInterface
会自动绑定到 DI\Container
,因此您无需进行任何配置即可注入它。
namespace Invoker;
interface InvokerInterface
{
public function call($callable, array $parameters = []);
}
Container::call()
可以调用任何可调用对象,这意味着:
- 闭包
- 函数
- 对象方法和静态方法
- 可调用对象 (实现 __invoke()的对象)
此外,你还可以调用:
- 可调用类的名称 :
$container->call('My\CallableClass')
- 对象的方法 (输入类名, 而不是对象):
$container->call(['MyClass', 'someMethod'])
在这两种情况下,容器会使用$container->get()
来解析'My\CallableClass'
和 'MyClass'
。
这样可以避免您使用更冗长的形式,例如:
$object = $container->get('My\CallableClass');
$container->call($object);
// 可以写成
$container->call('My\CallableClass');
injectOn()
有时您想对已创建对象注入依赖关系。
例如,某些旧框架不允许您控制如何创建控制器。
使用 injectOn
, 可以在创建对象之后让容器满足依赖性。
请记住,通常最好使用get()
或make()
来代替injectOn()
,我们仅在真正需要的地方使用injectOn()
。
例如:
class UserController extends BaseController
{
/**
* @Inject
* @var SomeService
*/
private $someService;
public function __construct()
{
// 框架不允许我们控制控制器的创建方式,因此
// 我们不能使用容器来创建控制器
// 因此,我们让容器来注入依赖项
$container->injectOn($this);
// 现在,依赖项已注入
$this->someService->doSomething();
}
}
您可能已经猜到了,您不能在此方法中使用构造函数注入。但是其他类型的注入(property 或者 setter)都将起作用,例如使用注解或者在配置文件中配置了对象。
扩展容器
如果您想深入研究容器的内部,请注意,只有带有@api
注释的类和接口才可供PHP-DI用户使用。所有其他类和接口都是内部类,您可以尝试使用它们,但即使在次要版本之间也不能保证向后兼容。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。