<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] # 說明 什么是系統服務?系統服務是對于程序要用到的類在使用前先進行類的標識的綁定,以便容器能夠對其進行解析(通過服務類的`register`方法),還有就是初始化一些參數、注冊路由等(不限于這些操作,主要是看一個類在使用之前的需要,進行一些配置,使用的是服務類的`boot`方法)。以下面要介紹到的`ModelService`為例,`ModelService`類提供服務,`ModelService`類主要對`Model`類的一些成員變量進行初始化(在`boot`方法中),為后面`Model`類的「出場」布置好「舞臺」。 下面先來看看系統自帶的服務,看看服務是怎么實現的。 # 內置服務 系統內置的服務有:`ModelService`、`PaginatorService`和`ValidateService`類,我們來看看它們是怎么被注冊和初始化的。 在`App::initialize()`有這么一段: ``` foreach ($this->initializers as $initializer) { $this->make($initializer)->init($this); } ``` 這里通過循環`App::initializers`的值,并使用容器類的`make`方法獲取每個`$initializer`的實例,然后調用實例對應的`init`方法。`App::initializers`成員變量的值為: ``` protected $initializers = [ Error::class, RegisterService::class, BootService::class, ]; ``` 這里重點關注后面兩個:服務注冊和服務初始化。 ## 服務注冊 執行`$this->make($initializer)->init($this)`,`$initializer`等于`RegisterService::class`時,調用該類中的`init`方法,該方法代碼如下: ``` 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); } } } ``` 服務注冊類中,定義了系統內置服務的值: ``` protected $services = [ PaginatorService::class, ValidateService::class, ModelService::class, ]; ``` 這三個服務和擴展包定義的服務將逐一被注冊,其注冊的方法`register`代碼如下: ``` public function register($service, bool $force = false) { // 比如 think\service\PaginatorService // getService方法判斷服務的實例是否存在于App::$services成員變量中 // 如果是則直接返回該實例 $registered = $this->getService($service); // 如果服務已注冊且不強制重新注冊,直接返回服務實例 if ($registered && !$force) { return $registered; } // 實例化該服務 // 比如 think\service\PaginatorService, // 該類沒有構造函數,其父類Service類有構造函數,需要傳入一個App類的實例 // 所以這里傳入$this(App類的實例)進行實例化 if (is_string($service)) { $service = new $service($this); } // 如果存在「register」方法,則調用之 if (method_exists($service, 'register')) { $service->register(); } // 如果存在「bind」屬性,添加容器標識綁定 if (property_exists($service, 'bind')) { $this->bind($service->bind); } // 保存服務實例 $this->services[] = $service; } ``` 詳細分析見代碼注釋。如果服務類定義了`register`方法,在服務注冊的時候會被執行,該方法通常是用于將服務綁定到容器;此外,也可以通過定義`bind`屬性的值來將服務綁定到容器。 服務逐個注冊之后,得到`App::services`的值大概是這樣的: ![ThinkPHP6 源碼閱讀(十二):系統服務](https://cdn.learnku.com/uploads/images/201909/01/27146/vVrTaWKoHK.PNG!/fw/1240) 每個服務的實例都包含一個`App`類的實例。 ## 服務初始化 執行`$this->make($initializer)->init($this)`,`$initializer`等于`BootService::class`時,調用該類中的`init`方法,該方法代碼如下: ``` public function init(App $app) { $app->boot(); } ``` 實際上是執行`App::boot()`: ``` public function boot(): void { array_walk($this->services, function ($service) { $this->bootService($service); }); } ``` 這里是將每個服務實例傳入bootService方法中。重點關注`bootService`方法: ``` public function bootService($service) { if (method_exists($service, 'boot')) { return $this->invoke([$service, 'boot']); } } ``` 這里調用服務實例對應的`boot`方法。接下來,我們以`ModelService`的`boot`方法為例,看看`boot`方法大概可以做哪些工作。`ModelService`的`boot`方法代碼如下: ``` public function boot() { // 設置Db對象 Model::setDb($this->app->db); // 設置Event對象 Model::setEvent($this->app->event); // 設置容器對象的依賴注入方法 Model::setInvoker([$this->app, 'invoke']); // 保存閉包到Model::maker Model::maker(function (Model $model) { //保存db對象 $db = $this->app->db; //保存$config對象 $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')); } }); } ``` 可以看出,這里都是對`Model`類的靜態成員進行初始化。這些靜態成員變量的訪問屬性為`protected`,所以,可以在`Model`類的子類中使用這些值。 # 自定義系統服務 接著,我們自己動手來寫一個簡單的系統服務。 * 定義被服務的對象(類) 創建一個文件:`app\common\MyServiceDemo.php`,寫入代碼如下: ``` <?php namespace app\common; class MyServiceDemo { //定義一個靜態成員變量 protected static $myStaticVar = '123'; // 設置該變量的值 public static function setVar($value){ self::$myStaticVar = $value; } //用于顯示該變量 public function showVar() { var_dump(self::$myStaticVar); } } ``` * 定義服務提供者 在項目根目錄,命令行執行`php think make:service MyService`,將會生成一個`app\service\MyService.php`文件,在其中寫入代碼: ``` <?php namespace app\service; use think\Service; use app\common\MyServiceDemo; class MyService extends Service { // 系統服務注冊的時候,執行register方法 public function register() { // 將綁定標識到對應的類 $this->app->bind('my_service', MyServiceDemo::class); } // 系統服務注冊之后,執行boot方法 public function boot() { // 將被服務類的一個靜態成員設置為另一個值 MyServiceDemo::setVar('456'); } } ``` * 配置系統服務 在`app\service.php`文件(如果沒有該文件則創建之),寫入: ``` <?php return [ '\app\service\MyService' ]; ``` * 在控制器中調用 創建一個控制器文件`app\controller\Demo.php`,寫入代碼: ``` <?php namespace app\controller; use app\BaseController; use app\common\MyServiceDemo; class Demo extends BaseController { public function testService(MyServiceDemo $demo){ // 因為在服務提供類app\service\MyService的boot方法中設置了$myStaticVar=‘456’\ // 所以這里輸出'456' $demo->showVar(); } public function testServiceDi(){ // 因為在服務提供類的register方法已經綁定了類標識到被服務類的映射 // 所以這里可以使用容器類的實例來訪問該標識,從而獲取被服務類的實例 // 這里也輸出‘456’ $this->app->my_service->showVar(); } } ``` 執行原理和分析見代碼注釋。另外說說自定義的服務配置是怎么加載的:`App::initialize()`中調用了`App::load()`方法,該方法結尾有這么一段: ``` if (is_file($appPath . 'service.php')) { $services = include $appPath . 'service.php'; foreach ($services as $service) { $this->register($service); } } ``` 正是在這里將我們自定義的服務加載進來并且注冊。 # 在Composer擴展包中使用服務 這里以`think-captcha`擴展包為例,該擴展使用了系統服務,其中,服務提供者為`think\captcha\CaptchaService`類,被服務的類為`think\captcha\Captcha`。 首先,項目根目錄先運行`composer require topthink/think-captcha`安裝擴展包;安裝完成后,我們查看`vendor\services.php`文件,發現新增一行: ``` return array ( 0 => 'think\\captcha\\CaptchaService', //新增 ); ``` 這是怎么做到的呢?這是因為在`vendor\topthink\think-captcha\composer.json`文件配置了: ``` "extra": { "think": { "services": [ "think\\captcha\\CaptchaService" ] } }, ``` 而在項目根目錄下的`composer.json`,有這樣的配置: ``` "scripts": { "post-autoload-dump": [ "@php think service:discover", "@php think vendor:publish" ] } ``` 擴展包安裝后,會執行這里的腳本,其中,跟這里的添加系統服務配置相關的是:`php think service:discover`。該指令執行的代碼在`vendor\topthink\framework\src\think\console\command\ServiceDiscover.php`,相關的代碼如下: ``` foreach ($packages as $package) { if (!empty($package['extra']['think']['services'])) { $services = array_merge($services, (array) $package['extra']['think']['services']); } } $header = '// This file is automatically generated at:' . date('Y-m-d H:i:s') . PHP_EOL . 'declare (strict_types = 1);' . PHP_EOL; $content = '<?php ' . PHP_EOL . $header . "return " . var_export($services, true) . ';'; file_put_contents($this->app->getRootPath() . 'vendor/services.php', $content); ``` 可以看出,擴展包如果有配置`['extra']['think']['services']`,也就是系統服務配置,都會被寫入到`vendor\services.php`文件,最終,所有服務在系統初始化的時候被加載、注冊和初始化。 分析完了擴展包中服務配置的實現和原理,接著我們看看`CaptchaService`服務提供類做了哪些初始化工作。該類只有一個`boot`方法,其代碼如下: ``` public function boot(Route $route) { // 配置路由 $route->get('captcha/[:config]', "\\think\\captcha\\CaptchaController@index"); // 添加一個驗證器 Validate::maker(function ($validate) { $validate->extend('captcha', function ($value) { return captcha_check($value); }, ':attribute錯誤!'); }); } ``` 有了以上的先行配置,我們就可以愉快地使用`Captcha`類了。 # 總結 使用系統服務有大大的好處和避免了直接修改類的壞處。從以上分析來看,個人覺得,使用系統服務,可以對一個類進行非入侵式的「配置」,如果哪天一個類的某些設定需要修改,我們不用直接修改這個類,只需要修改服務提供類就好了。對于擴展包來說,系統服務使其可以在擴展中靈活配置程序,達到開箱即用的效果。不過,有個缺點是系統服務類都要在程序初始化是進行實例化,如果一個系統的服務類很多,勢必影響程序的性能。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看