ThinkPHP6 核心分析:系统服务
什么是系统服务?系统服务是对于程序要用到的类在使用前先进行类的标识的绑定,以便容器能够对其进行解析(通过服务类的? 下面先来看看系统自带的服务,看看服务是怎么实现的。 ?
内置服务系统内置的服务有: 在? 1 foreach ($this->initializers as $initializer) { 2 $this->make($initializer)->init($this); 3 } ? 这里通过循环? 1 protected $initializers = [ 2 Error::class,3 RegisterService::class,4 BootService::class,5 ]; ? 这里重点关注后面两个:服务注册和服务初始化。 ?
服务注册执行? 1 public function init(App $app) 2 { 3 // 加载扩展包的服务 4 $file = $app->getRootPath() . 'vendor/services.php'; 5 6 $services = $this->services; 7 8 //合并,得到所有需要注册的服务 9 if (is_file($file)) { 10 $services = array_merge($services,include $file); 11 } 12 // 逐个注册服务 13 foreach ($services as $service) { 14 if (class_exists($service)) { 15 $app->register($service); 16 } 17 } 18 } ? 服务注册类中,定义了系统内置服务的值: 1 protected $services = [ 2 PaginatorService::class,3 ValidateService::class,4 ModelService::class,5 ]; ? 这三个服务和扩展包定义的服务将逐一被注册,其注册的方法? 1 public function register($service,bool $force = false) 2 { 3 // 比如 thinkservicePaginatorService 4 // getService方法判断服务的实例是否存在于App::$services成员变量中 5 // 如果是则直接返回该实例 6 $registered = $this->getService($service); 7 // 如果服务已注册且不强制重新注册,直接返回服务实例 8 if ($registered && !$force) { 9 return $registered; 10 } 11 // 实例化该服务 12 // 比如 thinkservicePaginatorService, 13 // 该类没有构造函数,其父类Service类有构造函数,需要传入一个App类的实例 14 // 所以这里传入$this(App类的实例)进行实例化 15 if (is_string($service)) { 16 $service = new $service($this); 17 } 18 // 如果存在「register」方法,则调用之 19 if (method_exists($service,'register')) { 20 $service->register(); 21 } 22 // 如果存在「bind」属性,添加容器标识绑定 23 if (property_exists($service,'bind')) { 24 $this->bind($service->bind); 25 } 26 // 保存服务实例 27 $this->services[] = $service; 28 } ? 详细分析见代码注释。如果服务类定义了? 服务逐个注册之后,得到? ? ? ? ?
每个服务的实例都包含一个? ?
服务初始化执行? 1 public function init(App $app) 2 { 3 $app->boot(); 4 } 5 实际上是执行 App::boot(): 6 7 public function boot(): void 8 { 9 array_walk($this->services,function ($service) { 10 $this->bootService($service); 11 }); 12 } ? 这里是将每个服务实例传入 bootService 方法中。重点关注? 1 public function bootService($service) 2 { 3 if (method_exists($service,'boot')) { 4 return $this->invoke([$service,'boot']); 5 } 6 } ? 这里调用服务实例对应的? 1 public function boot() 2 { 3 // 设置Db对象 4 Model::setDb($this->app->db); 5 // 设置Event对象 6 Model::setEvent($this->app->event); 7 // 设置容器对象的依赖注入方法 8 Model::setInvoker([$this->app,'invoke']); 9 // 保存闭包到Model::maker 10 Model::maker(function (Model $model) { 11 //保存db对象 12 $db = $this->app->db; 13 //保存$config对象 14 $config = $this->app->config; 15 // 是否需要自动写入时间戳 如果设置为字符串 则表示时间字段的类型 16 $isAutoWriteTimestamp = $model->getAutoWriteTimestamp(); 17 18 if (is_null($isAutoWriteTimestamp)) { 19 // 自动写入时间戳 (从配置文件获取) 20 $model->isAutoWriteTimestamp($config->get('database.auto_timestamp','timestamp')); 21 } 22 // 时间字段显示格式 23 $dateFormat = $model->getDateFormat(); 24 25 if (is_null($dateFormat)) { 26 // 设置时间戳格式 (从配置文件获取) 27 $model->setDateFormat($config->get('database.datetime_format','Y-m-d H:i:s')); 28 } 29 30 }); 31 } ? 可以看出,这里都是对? ?
自定义系统服务接着,我们自己动手来写一个简单的系统服务。
?
在 Composer 扩展包中使用服务这里以? 首先,项目根目录先运行? 1 return array ( 2 0 => 'thinkcaptchaCaptchaService',//新增 3 ); ? 这是怎么做到的呢?这是因为在? 1 "extra": { 2 "think": { 3 "services": [ 4 "thinkcaptchaCaptchaService" 5 ] 6 } 7 }, 8 而在项目根目录下的 composer.json,有这样的配置: 9 10 "scripts": { 11 "post-autoload-dump": [ 12 "@php think service:discover",13 "@php think vendor:publish" 14 ] 15 } ? 扩展包安装后,会执行这里的脚本,其中,跟这里的添加系统服务配置相关的是: 1 foreach ($packages as $package) { 2 if (!empty($package['extra']['think']['services'])) { 3 $services = array_merge($services,(array) $package['extra']['think']['services']); 4 } 5 } 6 7 $header = '// This file is automatically generated at:' . date('Y-m-d H:i:s') . PHP_EOL . 'declare (strict_types = 1);' . PHP_EOL; 8 9 $content = '<?php ' . PHP_EOL . $header . "return " . var_export($services,true) . ';'; 10 11 file_put_contents($this->app->getRootPath() . 'vendor/services.php',$content); ? 可以看出,扩展包如果有配置? 分析完了扩展包中服务配置的实现和原理,接着我们看看? 1 public function boot(Route $route) 2 { 3 // 配置路由 4 $route->get('captcha/[:config]',"thinkcaptchaCaptchaController@index"); 5 // 添加一个验证器 6 Validate::maker(function ($validate) { 7 $validate->extend('captcha',function ($value) { 8 return captcha_check($value); 9 },':attribute错误!'); 10 }); 11 } ? 有了以上的先行配置,我们就可以愉快地使用? ?
总结使用系统服务有大大的好处和避免了直接修改类的坏处。从以上分析来看,个人觉得,使用系统服务,可以对一个类进行非入侵式的「配置」,如果哪天一个类的某些设定需要修改,我们不用直接修改这个类,只需要修改服务提供类就好了。对于扩展包来说,系统服务使其可以在扩展中灵活配置程序,达到开箱即用的效果。不过,有个缺点是系统服务类都要在程序初始化是进行实例化,如果一个系统的服务类很多,势必影响程序的性能。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |