metrophp/metrodi

3.4.1 2018-03-26 23:04 UTC

README

小巧、智能的依赖注入器

tl;dr

  • 不使用类类型
  • 不依赖自动加载
  • 首先检查参数名称
  • 实际上使用类类型,但在检查参数名称之后
  • 自动注入以 'Service' 结尾的任何公共类变量

设置

Metro DI 可以通过创建对象或作为全局命名空间函数的单例使用。

	$cont = Metrodi_Container::getContainer();
	$cont->set('flaga', true);
	$cont->didef('logService', 'path/to/log.php', 'arg1', 'arg2', 'arg3');
	$log = $cont->make('logService');

使用全局函数它看起来是这样的

	_set('flaga', true);
	_didef('logService', 'path/to/log.php', 'arg1', 'arg2', 'arg3');
	$log = _make('logService');

事物

在 Metro DI 中,您使用 DI def 函数命名您的依赖项。一个未定义的事物将返回一个原型对象,该对象通过魔法 __call 记录对它的所有方法调用。

	$cart = _make('shoppingCartService');
	echo( get_class($cart) );  //  Metrodi_Proto

	_didef('shoppingCartService', 'path/to/my/cart.php');
	$cart = _make('shoppingCartService');
	echo( get_class($cart) );  //  Path_To_My_Cart

构造函数注入

当使用 _make 创建对象时,注入器将检查构造函数参数,并找到任何具有相同名称的已定义事物并将它们传递。为什么不使用类类型呢?想象一下尝试切换实际的实现对象。您必须更改包含旧类类型提示的每个文件,或者您必须创建一个充满接口的库,以便为未来可能的切换做好准备。

一些其他语言没有无类型的优势。使用变量的名称而不是类型提示(同时仍然回退到类型提示)允许进行最快的原型设计和最灵活的依赖实现更改。

	_didef('request',  '\Top\Quality\Request');
	_didef('response', '\Top\Quality\Response');

	class MyController {

		public function __construct($request, $response) {
			echo get_class($request);  // 'Top\Quality\Request';
		}
	}

传递参数

您可以在定义对象和创建对象时传递参数。

	namespace org\my\cart\service\abstract\concrete\interface;
	class Cart {
		public function __construct($idUser, $listItems, $timestamp=NULL) {
			$this->idUser    = $idUser;
			$this->listItems = $listItems;
			$this->timestamp = $timestamp;
		}
	}

	_didef('shoppingCartService', '\org\my\cart\service\abstract\concrete\interface\Cart', 'A', 'B');
	$cart1 = _make('shoppingCartService');
	echo $cart1->idUser;     // 'A'
	echo $cart1->listItems;  // 'B'
	echo $cart1->timestamp;  // null

	$cart2 = _make('shoppingCartService', 'C', 'D', time());
	echo $cart2->idUser;     // 'C'
	echo $cart2->listItems;  // 'D'
	echo $cart2->timestamp;  // 1234567890  (YMMV)

单例

单例和新的对象都可以通过 _make() 访问,但这取决于您如何使用 _make() 与 _didef()。

   _didef('singletonService', '\ns\locator\class', 'arg1', 'arg2');

   //later
   $ss1 = _make('singletonService');

   //same reference
   $ss2 = _make('singletonService');

如果您的对象不是固有的服务,并且每次创建时都需要新的构造函数参数,则可以在每次 _make() 调用中传递构造函数参数。每个唯一的参数组合将被哈希,并缓存生成的对象将在后续对 _make() 的调用中返回。

   _didef('user', '\ns\locator\class');

   //later
   $u1 = _make('user', 100);

   //new object
   $u2 = _make('user', 200);

您可以通过向 _didef() 提供参数来结合这两种方法,这些参数将被组合并用作后续 _make() 调用的默认值

   _didef('log', '\ns\locator\class', '/tmp/out.log');

   //later
   $mainLog    = _make('log');
   $specialLog = _make('log', '/var/log/special.log');

_makeNew() 函数将始终返回类定义的新实例,无论传递了什么参数。它简单地绕过任何实例缓存,并继续以与第一次调用 _make() 时相同的方式创建新对象。

   _didef('user', '\ns\user');

   //later
   $user1 = _make('user');
   $user2 = _makeNew('user'); //different instance

闭包

从 3.1.0 版本开始,可以将闭包或任何回调作为任何事物的定义传递。

    _didef('connection', function($c) {
       return new \Ns\Connection($c['username'], $c['password']);
    });

   $c = _make('connection', array('bob', 'secret');

匿名函数的结果将从 _make 返回而不是函数对象。此行为默认为单例行为,重复调用 _make('connection') 将返回相同的对象。

要获取新的引用并再次调用匿名函数,请使用 _makeNew()

    _didef('connection', function($c) {
        return new \Ns\Connection($c['username'], $c['password']);
    });

    $c  = _make('connection', array('bob', 'secret');
    $c2 = _makeNew('connection', array('alice', 'secret2');

服务提供者

当您从定义公共类变量且其中一个变量名称以 'Service' 结尾的类创建事物时,依赖注入器将自动将该服务的延迟加载对象注入到对象的变量中。

    class NullMailer {
        public function send() {
            print "Sending...\n";
        }
    }

    class Controller {
        public $emailService;
    }

    _didef('controller', 'Controller');
    _didef('emailService', new NullMailer());

    $c  = _make('controller');
    $c->emailService->send();