php的ioc容器,关于php:传递IoC容器的正确方法

我正在寻找DI和IoC;现在使用疙瘩。假设我在执行流程的早期定义了IoC

$container = new Injection\Container();

$container['config'] = function ($c) {

return new Config($c['loader']);

};

$container['request'] = function ($c) {

return new Request($c['config']);

};

...

并且路由器类call_user_func_array

//$class = 'Dog', $method = 'woof', $this->args = ['foo', 'bar']

call_user_func_array(array(new $class, $method), $this->args);

因此,在不知道IoC的情况下实例化新对象,但我仍然想重用一些定义的服务。

class Dog

{

public function woof($var1, $var2)

{

//$request = IoC service here

}

}

我的问题是:

什么是将IoC传递给类的正确方法(静态似乎是邪恶的...)或

是否有必要传递容器并存在其他方法/概念?

阅读一些不错的文章,但无法弄清楚

了解IoC容器和依赖注入

为什么不通过你的IoC容器?

更新

我这样做的邪恶方法是定义另一个将IoC保存在静态属性中的服务

$container['services'] = function ($c) {

return Services::create($c); //make the service

};

$container['services']; //call the service

并在以后访问它

class Dog

{

public function woof($var1, $var2)

{

$services = new Services();

$request = $services['request']; //retrieving the request service

}

}

更新2

决定使用最不危害的方式

//passing the container here

call_user_func_array(array(new $class($container), $method), $this->args);

并将参数存储在__constructor中

public $container;

public function __construct(Injection\Container $container)

{

$this->container = $container;

}

IoC通常使用2种模式,两者都是支持者。

依赖注入

服务定位器

然而,似乎越来越多的DI赢得了服务定位器模式。许多DI容器使得使用Service Locator变得更加困难,并且在文档中发出警告,不要走这条路。

在DI中,除非该类是组合根的一部分,否则永远不会将容器传递给类。组合根通过在应用程序的入口点处解析对象图来启动所有运动,并且从那里应用程序完全不知道DI容器(没有引用它)。请注意,此对象图可能包含创建类的运行时实例的抽象工厂(通过注入要从DI容器解析的函数或仅通过新建它们)。

服务定位器是频谱的另一端。通常,容器要么是静态的,要么作为唯一依赖项传递给类。以这种方式构建类可能更容易,但是当您实际需要配置DI容器时,您需要付出代价。

使用DI,类的依赖关系是显式的,因此您无需进一步查看构造函数参数。使用Service Locator,配置依赖项要复杂得多。这是主要原因(实际上有很多原因)为什么近年来它被认为是一种反模式。

因此,要回答您的问题,如果您想要遵循IoC的现代方法,请不要将IoC容器传递到应用程序中。而是在应用程序的入口点使用组合根来配置容器并构建对象图。

依赖注入示例

这里可以看到PHP中的完整示例。我在关于Pimple项目的讨论中找到了这个页面。

所以,举个例子:

class Dog

{

public function woof($var1, $var2)

{

//$request = IoC service here

}

}

您需要添加一个构造函数来接受您的$ request服务,以便您的类将接收它的实例。

class Dog

{

protected $request;

public function __construct(Request $request)

{

$this->request = $request;

}

public function woof($var1, $var2)

{

//Use $request here, disregard the IoC container

$this->request->doSomething()

}

}

然后,您将有一个部分,您可以在组合根中定义控制器。这是注入依赖项的地方。

$container = new Injection\Container();

$container['config'] = function ($c) {

return new Config($c['loader']);

};

$container['request'] = function ($c) {

return new Request($c['config']);

};

$container['dog'] = $container->factory(function ($c) {

return new Dog($c['request']);

});

$container['user_controller'] = $container->share(function ($container) {

return new UserController(

$container['dog'] //,

// $container['someOtherDependency']

);

});

如您所见,使用此方法,您的Dog类完全不知道DI容器。

谢谢你并同意你的意见;还有"在DI中,你永远不会将容器传递给类,除非该类是组合根的一部分。"并且"使用DI,类的依赖关系是显式的,所以你不需要看构造函数参数。"适用于我的情况。好的,但如果课程依赖于尚未知的服务怎么办?塞特犬注射?

What if the class depends on services that are yet unknown? - 使用IoC是关于抽象的编程,而不是具体的类型。这在许多方面都有所帮助,但主要是因为您可以提供一种方法,以便在不更改代码的情况下插入未来的未知具体类。它还可以创建用于测试的模拟。只要类遵守抽象契约(通常是接口),这是可能的。我想提供一个片段,但我不熟悉PHP ...

谢谢,但这并没有解决问题,因为我使用call_user_func_array并且只传递的东西是url参数。

想要跳过dog服务的定义,因为它们生活在应用程序而不是系统级别。

很好的解释。不过我对此表示怀疑。如果我的控制器C由IoC容器创建并且依赖D注入其中一个方法,那么会发生什么情况,然后在此方法中,控制器需要调用D的方法,而D依赖于D x6>作为参数。 C如何将参数A传递给D->methodOfDWhichUsesA(* A must be passed as parameter*)。假设A不是D的重要依赖项,但它必须仅用于D的methodOfDWhichUsesA方法。如何在不将容器传递给控制器??C的情况下处理?

没有更多信息,无法分辨。您可以使用抽象工厂创建它。但是,很可能如果它是应用程序的运行时状态而不是服务的一部分,那么您只需直接新建A。谷歌"newables vs injectables" - 关于这个话题有很多讨论。

这是我与工厂,单件,依赖注入器,服务定位器,接口注入器和其他模式的鬼混的例子。

config.php文件

return [

'services' => [

'request' => 'Request',

'view' => 'View',

'db' => function() {

return new DB( 'host', 'username', 'password', 'dbname' );

},

'translator' => function( $sm, $config ) {

return new Translator( $sm->db, $config );

},

'model' => function() {

return new ModelFactory( $sm );

}

],

'interfaces' => [

'iService' => function( $object, $sm ) {

$object->sm = $sm;

}

],

'translator' => [

'locale' => 'en_US',

]

];

服务容器

class ServiceManager {

private $services, $interfaces, $params;

function __construct( $config )

{

foreach( $config[ 'services' ] as $name => $value )

$this->add( $name, $value, isset( $config[ $name ] ) ? $config[ $name ] : null );

$this->interfaces = isset( $config[ 'interfaces' ] ) ? $config[ 'interfaces' ] : null;

}

function add( $name, $service, $params = null )

{

$this->services[ $name ] = $service;

$this->params[ $name ] = $params;

}

function __get( $name )

{

if ( is_string( $this->services[ $name ] ) )

$this->services[ $name ] = new $this->services[ $name ]( $this->params[ $name ] );

if ( is_callable( $this->services[ $name ] ) )

$this->services[ $name ] = $this->services[ $name ]( $this, $this->params[ $name ] );

foreach( $this->interfaces as $interface => $value ) {

if ( $this->services[ $name ] instanceof $interface )

$value( $this->services[ $name ], $this );

}

return $this->services[ $name ];

}

}

interface iService {}

class View implements iService {

function render() {

print_r( $this->sm );

}

};

class DB {

function __construct( $host, $username, $password, $dbname ) {}

};

class Translator {

function __construct( $db, $config ) {}

};

class ModelFactory {

function __construct( $sm ) {}

}

class App {

function __construct() {

$sm = new ServiceManager( include 'config.php' );

$sm->view->render();

}

}

new App;

我一直在考虑写一个解释和我对主题的看法,但由于我不擅长写作,关于这些主题已经有太多的意见(其中很大一部分是关于邪恶和反对的东西),我将离开这个例子,它可能对某人有用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值