以前用过hook吧,钩子的作用和事件是一个性质的,你可以把事件当作一个闭包或者匿名函数来使用,就能得到自己想要的结果了。
闭包函数和匿名函数的作用域属于自身,通过反射可以bindTo当前的方法或者类,进而获取其作用域。
事件event方法就是将before_select事件类绑定一个闭包函数,当触发器调用before_select时也就执行了闭包函数,闭包内部使用return返回的时当前作用域内的结果。也就是执行before_select就是在执行闭包函数function(){},其返回值获得了调用event类的那个类的作用域,按照框架的设计,是获得了当前调用模型类的作用域。
如果上边我将的太生涩了,都是文字能力太差的原因,建议你再看一下闭包函数和其作用域,另外模型事件的定义是可以参照源代码,找到trigger('before_select)(一般都是用动态调用的方式,你要跑一下xdebug)你就找到你要的结果了。
另外,再啰嗦一下框架的Model加载,前后叙述省略一些和你提出的问题不大的过程,
首先,Model是通过App容器内make()实例后,经过Service的register()注册成ModelService被加载进框架的,查看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内执行boot()时被初始化的。
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)) {
// 自动写入时间戳
$model->isAutoWriteTimestamp($config->get('database.auto_timestamp', 'timestamp'));
}
$dateFormat = $model->getDateFormat();
if (is_null($dateFormat)) {
// 设置时间戳格式
$model->setDateFormat($config->get('database.datetime_format', 'Y-m-d H:i:s'));
}
});
}
你可以看到通过静态调用方法setEvent()将Event对象(也是整个框架底层结构的事件系统)加载进来,其中当然就包括模型事件,因为模型事件在整个模型系统中只是一个子集(看作一个属性)。
在整个框架内部的任意位置你随时都可以执行Db::event()或模型内部静态调用setEvent(),不过模型内部的静态调用setEvent()创建的任务是独立于框架系统事件任务的,但是框架系统事件可以随时调用它,具体内容自行看trait ModelEvent类这里不赘述了。
明白了以上的流程和逻辑关系之后,再回头看你自己对模型事件的自定义内容,事件系统本身就是通过绑定的方式进行关联。对于模型事件来说,也不例外
public function event(string $event, callable $callback): void
{
$this->event[$event][] = $callback;
}
这也就是在模型事件trigger()触发$event是,会执行一个$callback,如果你没有return就不存在任何返回值,只是执行了函数体,函数体也是你所有的预期操作和执行,而加上return只有一种情况,就是不执行当前事件之后的事件了,给一个false就可以。对于事件$event参与的执行,无法给你任何的反馈,你只是进行了一次hook而已。
另外附加一下trigger()的调用代码
public function trigger(string $event, $params = null)
{
if (isset($this->event[$event])) {
foreach ($this->event[$event] as $callback) {
call_user_func_array($callback, [$this]);
}
}
}
以上内容没有进行二次校对,错别字和语病请担待。
补充一句,看trigger()里的if判断,就是你不理解的return值,给了false后边的循环就不进行了。注意,这里用的是isset判断!!!只要定义过模型事件,isset就是true,模型事件内没有return和return任何除false以外的值一样可以执行,即使你return 0;都没关系。
$this->event[$event]就是将所有模型事件的类型存到数组中的索引中,而模型事件的闭包结果存在$this->event[$event][]中,后边的循环就是遍历整个数组,完成所有事件的执行。