Yii2 实例管理
Yii2框架没有提供类似ZendFramework3中提供的ServiceManager.那么Yii2框架是如何管理对象实例的呢?
长时间使用Yii2框架的童鞋一定已经适应了对象中的public变量,以及一个来自yii\base\Component的__set()魔术方法来完成对象构造的过程,但是新鸟如何使用这一切来实例化一个对象呢?
Yii2最擅长的是extends多个对象使他们拥有多个父类的特征.那么习惯了ZendFramework3这种纯粹OOP的童鞋,有没有方法既可以尽快适应Yii2的简约,又坚定的不侵犯对象变量的私有性,甚至还能优雅的用注入取代继承呢?
以下比较对象默认为ZendFramework3.
Yii2 对象的实例化
在ZendFramework3中,对象的实例化是我见过相对复杂的.
- 首先ServiceManager会读取所有已配置模块的Module对象(每个模块提供给ServiceManager的入口),并根据loadModules,loadModule等事件,以特定(可配置的,通过引用不同的内置Interface)方式获取模块配置文件.
- 获得配置文件时,ServiceManager会检查依赖(如果需要).
- 一切顺利后,ServiceManager会依照配置文件,寻找这个模块或类的工厂.工厂是一个callable,可以极其复杂,因为每个工厂会接受到整个项目的容器,也就是ServiceManager,也可以是简单的内置,InvokeFactory.
- 最终ServiceManager会自动的将工厂的产品,也就是这个模块的实例缓存到自身的变量中,以便全局可以访问.
可见ServiceManager是一个综合体,用于对象实例的管理,充当全局容器(里面甚至包含所有的配置文件和事件管理模块等所有框架的内核).
你肯定会担心性能损耗,在小型项目中这种损耗是有的,就像快速排序,面对大宗数据才是它大展身手的时候.也就是说,随着项目体量的增加,这种损耗的递增会远远低于体量的递增.
缺点:学习成本很高.
那么Yii2中有这样的机制吗?答曰,有.
Yii2中居然用一个近似让人啼笑皆非的方式解决了这个全局容器的需求.就是那个哪里都能看到的静态调用Yii.
use Yii;
很聪明!但是缺少了保障:
-
安全性.对于全部项目,Yii这个容器始终是可见的,没有注入的安全性,不注入你休想碰全局.Yii2的问题在于安全,尤其是多人开发的项目,例如通过
Yii::$app->user->setIdentity($identity);
轻松改变框架的当前登录用户,伪造当前用户. -
稳定性.这个是我的猜测,将整个框架直接交给一个Yii,真的好吗?做法等同于将整个框架放入了一个PHP数组,并且这还是个全局的数组.
优点:学习成本很低.
Yii2在设计上表达了一个简单粗暴的观点,简单大于一切,甚至性能!
Yii2在对象的实例化时居然使用了Reflection[泪目.jpg],你没有看错,就是那个性能如老狗的反射.为什么呢?
就为了让你少些代码.
Yii::createObject([
'class' => 'foo\bar',
'name' => 'Bob'
]);
以上代码实例化了一个foo\bar;对象,并执行了$bar->name = 'Bob';
,怎么样,是不是很简单?这一切都是Reflection的功劳.当然Yii::createObject()
还有一下用法,更加神奇.
//In foo/bar.php
class bar
{
public function __construct(SomeInterface $param1, $param2 = 'nothing')
{
//something to do.
}
}
//In some place.
Yii::createObject('foo\bar', [
'param1' => $someClass,//instance from SomeInterface
])
可见.Yii::createObject()
还可以对你的__construct
赋值.不但会检查参数列表的默认值,对象类型,而且还会为提供可变参数的构造函数检查PHP'版本.很贴心有木有?
但是这一切都是Reflection提供的.也是在PHP应用层能解决的唯一途径.
在动不动就实例化几百个对象的应用中,如果追求性能极致的小伙伴,最好还是自己实现实例管理工具.我也会在后续的空闲时间实现这个功能,请关注本人GitHub.
Yii2 对象实例的缓存
var_dump(Yii::$container->get(Test::class) === Yii::$container->get(Test::class));
上例返回false
[懵逼.jpg],怎么回事,Yii2不缓存对象吗?那么Yii2的单例如何实现呢?
原来,Yii2中你要想让自己的对象被缓存,只有先Yii::$container->set()
,再Yii::$container->get()
.set
和get
的参数列表是对称的.
也就是说,Yii2不会自动给你缓存对象,实现单例.可能是怕误导不知道设计模式的初学者,为了对新手友好吧.
set
和get
的具体用法在\yii\di\Container
的注释中有非诚详细的说明.
值得一说的是,作者也意识到频繁的使用Reflection对性能的影响,所以在每次实例化的时候会自动的缓存对象的Reflection.这点还是非常温馨的.
Yii2中最常见的实例化对象的方法
Yii::createObject();
这个东西,这其实只是简单的对yii\di\Containre
的get()
的封装.
这个方法有两个参数:
- 对象设置
这是个数组,除了'class' => 'SomeClass'
表示这个类的名字外,其他全部代表这个类的属性. - constructor参数列表
这个没有什么好解释的,就是构造函数的参数列表写成数组的形式而已.
总结
其实作文还没有写完,我并没有去yy怎样去自己实现一个实例容器模块.因为我想在模块出现在GitHub上的时候,结合README详细说明.并在以后的开发中不断改进.
毫无疑问,Yii2是个非常精妙的框架,脾气也非常清晰,易用大于一切,毕竟用框架开发的项目不会是超大项目.如果追求性能上的极致,可能任何框架都没法满足你的要求,甚至是PHP本身.到那时候的讨论可能涉及到更深的层次.就目前而言,就目前的应用层次,看待每一个框架,你会发现每个作者的思想都有很独特的地方,可能这个思想本身就已经大于框架本身的意义了,尤其是每个作者展现给我们的,在现实和理想冲突的时候,做出的妥协.
网友评论