Yii2 实例管理(重新编辑版)

Yii2 实例管理

Yii2框架没有提供类似ZendFramework3中提供的ServiceManager.那么Yii2框架是如何管理对象实例的呢?

长时间使用Yii2框架的童鞋一定已经适应了对象中的public变量,以及一个来自yii\base\Component的__set()魔术方法来完成对象构造的过程,但是新鸟如何使用这一切来实例化一个对象呢?

Yii2最擅长的是extends多个对象使他们拥有多个父类的特征.那么习惯了ZendFramework3这种纯粹OOP的童鞋,有没有方法既可以尽快适应Yii2的简约,又坚定的不侵犯对象变量的私有性,甚至还能优雅的用注入取代继承呢?

以下比较对象默认为ZendFramework3.

Yii2 对象的实例化

在ZendFramework3中,对象的实例化是我见过相对复杂的.

  1. 首先ServiceManager会读取所有已配置模块的Module对象(每个模块提供给ServiceManager的入口),并根据loadModules,loadModule等事件,以特定(可配置的,通过引用不同的内置Interface)方式获取模块配置文件.
  2. 获得配置文件时,ServiceManager会检查依赖(如果需要).
  3. 一切顺利后,ServiceManager会依照配置文件,寻找这个模块或类的工厂.工厂是一个callable,可以极其复杂,因为每个工厂会接受到整个项目的容器,也就是ServiceManager,也可以是简单的内置,InvokeFactory.
  4. 最终ServiceManager会自动的将工厂的产品,也就是这个模块的实例缓存到自身的变量中,以便全局可以访问.

可见ServiceManager是一个综合体,用于对象实例的管理,充当全局容器(里面甚至包含所有的配置文件和事件管理模块等所有框架的内核).

你肯定会担心性能损耗,在小型项目中这种损耗是有的,就像快速排序,面对大宗数据才是它大展身手的时候.也就是说,随着项目体量的增加,这种损耗的递增会远远低于体量的递增.

缺点:学习成本很高.


那么Yii2中有这样的机制吗?答曰,有.

Yii2中居然用一个近似让人啼笑皆非的方式解决了这个全局容器的需求.就是那个哪里都能看到的静态调用Yii.

use Yii;

很聪明!但是缺少了保障:

  1. 安全性.对于全部项目,Yii这个容器始终是可见的,没有注入的安全性,不注入你休想碰全局.Yii2的问题在于安全,尤其是多人开发的项目,例如通过Yii::$app->user->setIdentity($identity);轻松改变框架的当前登录用户,伪造当前用户.

  2. 稳定性.这个是我的猜测,将整个框架直接交给一个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().setget的参数列表是对称的.

也就是说,Yii2不会自动给你缓存对象,实现单例.可能是怕误导不知道设计模式的初学者,为了对新手友好吧.

setget的具体用法在\yii\di\Container的注释中有非诚详细的说明.

值得一说的是,作者也意识到频繁的使用Reflection对性能的影响,所以在每次实例化的时候会自动的缓存对象的Reflection.这点还是非常温馨的.


Yii2中最常见的实例化对象的方法

Yii::createObject();

这个东西,这其实只是简单的对yii\di\Containreget()的封装.

这个方法有两个参数:

  1. 对象设置
    这是个数组,除了'class' => 'SomeClass'表示这个类的名字外,其他全部代表这个类的属性.
  2. constructor参数列表
    这个没有什么好解释的,就是构造函数的参数列表写成数组的形式而已.

总结

其实作文还没有写完,我并没有去yy怎样去自己实现一个实例容器模块.因为我想在模块出现在GitHub上的时候,结合README详细说明.并在以后的开发中不断改进.

毫无疑问,Yii2是个非常精妙的框架,脾气也非常清晰,易用大于一切,毕竟用框架开发的项目不会是超大项目.如果追求性能上的极致,可能任何框架都没法满足你的要求,甚至是PHP本身.到那时候的讨论可能涉及到更深的层次.就目前而言,就目前的应用层次,看待每一个框架,你会发现每个作者的思想都有很独特的地方,可能这个思想本身就已经大于框架本身的意义了,尤其是每个作者展现给我们的,在现实和理想冲突的时候,做出的妥协.

此处评论已关闭