Yii2 實例管理
Yii2框架沒有提供類似ZendFramework3中提供的ServiceManager.那么Yii2框架是如何管理對象實例的呢?
長時間使用Yii2框架的童鞋一定已經(jīng)適應(yīng)了對象中的public變量,以及一個來自yii\base\Component的__set()魔術(shù)方法來完成對象構(gòu)造的過程,但是新鳥如何使用這一切來實例化一個對象呢?
Yii2最擅長的是extends多個對象使他們擁有多個父類的特征.那么習(xí)慣了ZendFramework3這種純粹OOP的童鞋,有沒有方法既可以盡快適應(yīng)Yii2的簡約,又堅定的不侵犯對象變量的私有性,甚至還能優(yōu)雅的用注入取代繼承呢?
以下比較對象默認(rèn)為ZendFramework3.
Yii2 對象的實例化
在ZendFramework3中,對象的實例化是我見過相對復(fù)雜的.
- 首先ServiceManager會讀取所有已配置模塊的Module對象(每個模塊提供給ServiceManager的入口),并根據(jù)loadModules,loadModule等事件,以特定(可配置的,通過引用不同的內(nèi)置Interface)方式獲取模塊配置文件.
- 獲得配置文件時,ServiceManager會檢查依賴(如果需要).
- 一切順利后,ServiceManager會依照配置文件,尋找這個模塊或類的工廠.工廠是一個callable,可以極其復(fù)雜,因為每個工廠會接受到整個項目的容器,也就是ServiceManager,也可以是簡單的內(nèi)置,InvokeFactory.
- 最終ServiceManager會自動的將工廠的產(chǎn)品,也就是這個模塊的實例緩存到自身的變量中,以便全局可以訪問.
可見ServiceManager是一個綜合體,用于對象實例的管理,充當(dāng)全局容器(里面甚至包含所有的配置文件和事件管理模塊等所有框架的內(nèi)核).
你肯定會擔(dān)心性能損耗,在小型項目中這種損耗是有的,就像快速排序,面對大宗數(shù)據(jù)才是它大展身手的時候.也就是說,隨著項目體量的增加,這種損耗的遞增會遠(yuǎn)遠(yuǎn)低于體量的遞增.
缺點:學(xué)習(xí)成本很高.
那么Yii2中有這樣的機(jī)制嗎?答曰,有.
Yii2中居然用一個近似讓人啼笑皆非的方式解決了這個全局容器的需求.就是那個哪里都能看到的靜態(tài)調(diào)用Yii.
use Yii;
很聰明!但是缺少了保障:
安全性.對于全部項目,Yii這個容器始終是可見的,沒有注入的安全性,不注入你休想碰全局.Yii2的問題在于安全,尤其是多人開發(fā)的項目,例如通過
Yii::$app->user->setIdentity($identity);輕松改變框架的當(dāng)前登錄用戶,偽造當(dāng)前用戶.穩(wěn)定性.這個是我的猜測,將整個框架直接交給一個Yii,真的好嗎?做法等同于將整個框架放入了一個PHP數(shù)組,并且這還是個全局的數(shù)組.
優(yōu)點:學(xué)習(xí)成本很低.
Yii2在設(shè)計上表達(dá)了一個簡單粗暴的觀點,簡單大于一切,甚至性能!
Yii2在對象的實例化時居然使用了Reflection[淚目.jpg],你沒有看錯,就是那個性能如老狗的反射.為什么呢?
就為了讓你少些代碼.
Yii::createObject([
'class' => 'foo\bar',
'name' => 'Bob'
]);
以上代碼實例化了一個foo\bar;對象,并執(zhí)行了$bar->name = 'Bob';,怎么樣,是不是很簡單?這一切都是Reflection的功勞.當(dāng)然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賦值.不但會檢查參數(shù)列表的默認(rèn)值,對象類型,而且還會為提供可變參數(shù)的構(gòu)造函數(shù)檢查PHP'版本.很貼心有木有?
但是這一切都是Reflection提供的.也是在PHP應(yīng)用層能解決的唯一途徑.
在動不動就實例化幾百個對象的應(yīng)用中,如果追求性能極致的小伙伴,最好還是自己實現(xiàn)實例管理工具.我也會在后續(xù)的空閑時間實現(xiàn)這個功能,請關(guān)注本人GitHub.
Yii2 對象實例的緩存
var_dump(Yii::$container->get(Test::class) === Yii::$container->get(Test::class));
上例返回false[懵逼.jpg],怎么回事,Yii2不緩存對象嗎?那么Yii2的單例如何實現(xiàn)呢?
原來,Yii2中你要想讓自己的對象被緩存,只有先Yii::$container->set(),再Yii::$container->get().set和get的參數(shù)列表是對稱的.
也就是說,Yii2不會自動給你緩存對象,實現(xiàn)單例.可能是怕誤導(dǎo)不知道設(shè)計模式的初學(xué)者,為了對新手友好吧.
set和get的具體用法在\yii\di\Container的注釋中有非誠詳細(xì)的說明.
值得一說的是,作者也意識到頻繁的使用Reflection對性能的影響,所以在每次實例化的時候會自動的緩存對象的Reflection.這點還是非常溫馨的.
Yii2中最常見的實例化對象的方法
Yii::createObject();
這個東西,這其實只是簡單的對yii\di\Containre的get()的封裝.
這個方法有兩個參數(shù):
- 對象設(shè)置
這是個數(shù)組,除了'class' => 'SomeClass'表示這個類的名字外,其他全部代表這個類的屬性. - constructor參數(shù)列表
這個沒有什么好解釋的,就是構(gòu)造函數(shù)的參數(shù)列表寫成數(shù)組的形式而已.
總結(jié)
其實作文還沒有寫完,我并沒有去yy怎樣去自己實現(xiàn)一個實例容器模塊.因為我想在模塊出現(xiàn)在GitHub上的時候,結(jié)合README詳細(xì)說明.并在以后的開發(fā)中不斷改進(jìn).
毫無疑問,Yii2是個非常精妙的框架,脾氣也非常清晰,易用大于一切,畢竟用框架開發(fā)的項目不會是超大項目.如果追求性能上的極致,可能任何框架都沒法滿足你的要求,甚至是PHP本身.到那時候的討論可能涉及到更深的層次.就目前而言,就目前的應(yīng)用層次,看待每一個框架,你會發(fā)現(xiàn)每個作者的思想都有很獨特的地方,可能這個思想本身就已經(jīng)大于框架本身的意義了,尤其是每個作者展現(xiàn)給我們的,在現(xiàn)實和理想沖突的時候,做出的妥協(xié).