看 phalcon 的源码,关于 Di :
假设:
$di = new Di();
// set
$di->set('request', function(){
return new Request();
});
// get
$request = $di->get('request');
看了下大概流程:
1 、 set() 创建 Service 对象
2 、 get() 拿到之前创建的 Service 对象,调用 resolve(),里面 call_user_func() 执行匿名函数
3 、 get() 里给组件 setDI($this) 并返回这个组件对象
里面有个很奇怪的地方:
resolve() 里面会用 \Closure::bind() 把 $di 对象交给匿名函数,接着才 call_user_func ,但是 $di->get() 里面却通过 setDI() 来注入 $di 对象,这是何必呢、、、
既然把 $di 对象 bind() 回去了,我就可以在匿名函数里直接 return new Request($this); 啊,这样每个组件继承一个基类,基类的构造函数负责给自己的 protected $_di; 赋值啊、、、
求解!
di: https://github.com/phalcon/cphalcon/blob/master/phalcon/di.zep
service: https://github.com/phalcon/cphalcon/blob/master/phalcon/di/service.zep
另外还有个很奇怪的问题,shared 的组件是单例的,但是这个单例是针对于存放它的 Service,Di 中 setShared 创建出来的 Service 并不是单例的,而是直接覆盖的:
let service = new Service(name, definition, shared),
this->_services[name] = service;
1
jerray 2016-08-16 20:22:52 +08:00 1
1. 只有当 instance 实现了 InjectionAwareInterface 接口的时候才会执行 setDI 。通过 set 方法创建的 Service 对象并没有实现这个接口
2. setShared 只是包装了一下 set : setShared(name, definition) -> set(name, definition, true) 3. 这里的 Service 是具体实现的包装器,根据 definition resolve 出来的对象才是真正运行时用到的 Service 4. shared 组件是单例的, Service 包装器在 resolve 的时候判断当前组件是否是单例,如果是并且已经实例化了,直接返回实例。如果未实例化,会根据 definition 进行实例化 5. 看上去同一个名字的组件只能注册一个 6. 我不会 Zephir 好像并没有哪里奇怪 |
2
bombless 2016-08-16 21:25:07 +08:00 via Android 1
它在全局有个默认的 di ,估计最开始设计的时候是所有组件都用这个 di ,后面才改成这样的。
我感觉 phalcon 很多地方都不符合直觉的 |