以前用過hook吧腹尖,鉤子的作用和事件是一個(gè)性質(zhì)的柳恐,你可以把事件當(dāng)作一個(gè)閉包或者匿名函數(shù)來使用伐脖,就能得到自己想要的結(jié)果了。
閉包函數(shù)和匿名函數(shù)的作用域?qū)儆谧陨砝稚瑁ㄟ^反射可以bindTo當(dāng)前的方法或者類讼庇,進(jìn)而獲取其作用域。
事件event方法就是將before_select事件類綁定一個(gè)閉包函數(shù)近尚,當(dāng)觸發(fā)器調(diào)用before_select時(shí)也就執(zhí)行了閉包函數(shù)蠕啄,閉包內(nèi)部使用return返回的時(shí)當(dāng)前作用域內(nèi)的結(jié)果。也就是執(zhí)行before_select就是在執(zhí)行閉包函數(shù)function(){}戈锻,其返回值獲得了調(diào)用event類的那個(gè)類的作用域歼跟,按照框架的設(shè)計(jì),是獲得了當(dāng)前調(diào)用模型類的作用域格遭。
如果上邊我將的太生澀了哈街,都是文字能力太差的原因,建議你再看一下閉包函數(shù)和其作用域拒迅,另外模型事件的定義是可以參照源代碼骚秦,找到trigger('before_select)(一般都是用動(dòng)態(tài)調(diào)用的方式,你要跑一下xdebug)你就找到你要的結(jié)果了璧微。
另外作箍,再啰嗦一下框架的Model加載,前后敘述省略一些和你提出的問題不大的過程前硫,
首先胞得,Model是通過App容器內(nèi)make()實(shí)例后,經(jīng)過Service的register()注冊成ModelService被加載進(jìn)框架的屹电,查看app.php的initialize()最后的foreach就看到了阶剑。
????????date_default_timezone_set($this->config->get('app.default_timezone',?'Asia/Shanghai'));
????????//?初始化
????????foreach?($this->initializers?as?$initializer)?{
????????????$this->make($initializer)->init($this);
????????}
????????return?$this;
????}
????protected?$services?=?[
????????PaginatorService::class,
????????ValidateService::class,
????????ModelService::class,
????];
????public?function?init(App?$app)
????{
????????$file?=?$app->getRootPath()?.?'vendor/services.php';
????????$services?=?$this->services;
????????if?(is_file($file))?{
????????????$services?=?array_merge($services,?include?$file);
????????}
????????foreach?($services?as?$service)?{
????????????if?(class_exists($service))?{
????????????????$app->register($service);
????????????}
????????}
????}
其次,Model模型是在ModelService內(nèi)執(zhí)行boot()時(shí)被初始化的嗤详。
????public?function?boot()
????{
????????Model::setDb($this->app->db);
????????Model::setEvent($this->app->event);
????????Model::setInvoker([$this->app,?'invoke']);
????????Model::maker(function?(Model?$model)?{
????????????$config?=?$this->app->config;
????????????$isAutoWriteTimestamp?=?$model->getAutoWriteTimestamp();
????????????if?(is_null($isAutoWriteTimestamp))?{
????????????????//?自動(dòng)寫入時(shí)間戳
????????????????$model->isAutoWriteTimestamp($config->get('database.auto_timestamp',?'timestamp'));
????????????}
????????????$dateFormat?=?$model->getDateFormat();
????????????if?(is_null($dateFormat))?{
????????????????//?設(shè)置時(shí)間戳格式
????????????????$model->setDateFormat($config->get('database.datetime_format',?'Y-m-d?H:i:s'));
????????????}
????????});
????}
你可以看到通過靜態(tài)調(diào)用方法setEvent()將Event對象(也是整個(gè)框架底層結(jié)構(gòu)的事件系統(tǒng))加載進(jìn)來,其中當(dāng)然就包括模型事件瓷炮,因?yàn)槟P褪录谡麄€(gè)模型系統(tǒng)中只是一個(gè)子集(看作一個(gè)屬性)葱色。
在整個(gè)框架內(nèi)部的任意位置你隨時(shí)都可以執(zhí)行Db::event()或模型內(nèi)部靜態(tài)調(diào)用setEvent(),不過模型內(nèi)部的靜態(tài)調(diào)用setEvent()創(chuàng)建的任務(wù)是獨(dú)立于框架系統(tǒng)事件任務(wù)的娘香,但是框架系統(tǒng)事件可以隨時(shí)調(diào)用它苍狰,具體內(nèi)容自行看trait ModelEvent類這里不贅述了。
明白了以上的流程和邏輯關(guān)系之后烘绽,再回頭看你自己對模型事件的自定義內(nèi)容淋昭,事件系統(tǒng)本身就是通過綁定的方式進(jìn)行關(guān)聯(lián)。對于模型事件來說安接,也不例外
????public?function?event(string?$event,?callable?$callback):?void
????{
????????$this->event[$event][]?=?$callback;
????}
這也就是在模型事件trigger()觸發(fā)$event是翔忽,會(huì)執(zhí)行一個(gè)$callback,如果你沒有return就不存在任何返回值,只是執(zhí)行了函數(shù)體歇式,函數(shù)體也是你所有的預(yù)期操作和執(zhí)行驶悟,而加上return只有一種情況,就是不執(zhí)行當(dāng)前事件之后的事件了材失,給一個(gè)false就可以痕鳍。對于事件$event參與的執(zhí)行,無法給你任何的反饋龙巨,你只是進(jìn)行了一次hook而已笼呆。
另外附加一下trigger()的調(diào)用代碼
????public?function?trigger(string?$event,?$params?=?null)
????{
????????if?(isset($this->event[$event]))?{
????????????foreach?($this->event[$event]?as?$callback)?{
????????????????call_user_func_array($callback,?[$this]);
????????????}
????????}
????}
以上內(nèi)容沒有進(jìn)行二次校對,錯(cuò)別字和語病請擔(dān)待旨别。
補(bǔ)充一句诗赌,看trigger()里的if判斷,就是你不理解的return值昼榛,給了false后邊的循環(huán)就不進(jìn)行了境肾。注意,這里用的是isset判斷5ㄓ臁0掠鳌!只要定義過模型事件非迹,isset就是true环鲤,模型事件內(nèi)沒有return和return任何除false以外的值一樣可以執(zhí)行,即使你return 0憎兽;都沒關(guān)系冷离。
$this->event[$event]就是將所有模型事件的類型存到數(shù)組中的索引中,而模型事件的閉包結(jié)果存在$this->event[$event][]中纯命,后邊的循環(huán)就是遍歷整個(gè)數(shù)組西剥,完成所有事件的執(zhí)行。