<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] 前言 這篇文章我們開始講laravel框架中的門面Facade,什么是門面呢?官方文檔: ??Facades)為應用程序的服務容器中可用的類提供了一個「靜態」接口。Laravel 自帶了很多 facades ,幾乎可以用來訪問到 Laravel 中所有的服務。Laravel facades 實際上是服務容器中那些底層類的「靜態代理」,相比于傳統的靜態方法, facades 在提供了簡潔且豐富的語法同時,還帶來了更好的可測試性和擴展性。 ??什么意思呢?首先,我們要知道laravel框架的核心就是個Ioc容器即[服務容器](https://d.laravel-china.org/docs/5.4/container),功能類似于一個工廠模式,是個高級版的工廠。laravel的其他功能例如路由、緩存、日志、數據庫其實都是類似于插件或者零件一樣,叫做[服務](https://d.laravel-china.org/docs/5.4/providers)。Ioc容器主要的作用就是生產各種零件,就是提供各個服務。在laravel中,如果我們想要用某個服務,該怎么辦呢?最簡單的辦法就是調用服務容器的make函數,或者利用依賴注入,或者就是今天要講的門面Facade。門面相對于其他方法來說,最大的特點就是簡潔,例如我們經常使用的Router,如果利用服務容器的make: ~~~ App::make('router')->get('/', function () { return view('welcome'); }); ~~~ 如果利用門面: ~~~ Route::get('/', function () { return view('welcome'); }); ~~~ 可以看出代碼更加簡潔。其實,下面我們就會介紹門面最后調用的函數也是服務容器的make函數。 # Facade的原理 ??我們以Route為例,來講解一下門面Facade的原理與實現。我們先來看Route的門面類: ~~~ class Route extends Facade { protected static function getFacadeAccessor() { return 'router'; } } ~~~ 很簡單吧?其實每個門面類也就是重定義一下getFacadeAccessor函數就行了,這個函數返回服務的唯一名稱:router。需要注意的是要確保這個名稱可以用服務容器的make函數創建成功(App::make(‘router’)),原因我們馬上就會講到。 ??那么當我們寫出Route::get()這樣的語句時,到底發生了什么呢?奧秘就在基類Facade中。 ~~~ public static function __callStatic($method, $args) { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } return $instance->$method(...$args); } ~~~ 當運行Route::get()時,發現門面Route沒有靜態get()函數,PHP就會調用這個魔術函數__callStatic。我們看到這個魔術函數做了兩件事:獲得對象實例,利用對象調用get()函數。首先先看看如何獲得對象實例的: ~~~ public static function getFacadeRoot() { return static::resolveFacadeInstance(static::getFacadeAccessor()); } protected static function getFacadeAccessor() { throw new RuntimeException('Facade does not implement getFacadeAccessor method.'); } protected static function resolveFacadeInstance($name) { if (is_object($name)) { return $name; } if (isset(static::$resolvedInstance[$name])) { return static::$resolvedInstance[$name]; } return static::$resolvedInstance[$name] = static::$app[$name]; } ~~~ 我們看到基類getFacadeRoot()調用了getFacadeAccessor(),也就是我們的服務重載的函數,如果調用了基類的getFacadeAccessor,就會拋出異常。在我們的例子里getFacadeAccessor()返回了“router”,接下來getFacadeRoot()又調用了resolveFacadeInstance()。在這個函數里重點就是 ~~~ return static::$resolvedInstance[$name] = static::$app[$name]; ~~~ 我們看到,在這里利用了$app也就是服務容器創建了“router”,創建成功后放入$resolvedInstance作為緩存,以便以后快速加載。 ??好了,Facade的原理到這里就講完了,但是到這里我們有個疑惑,為什么代碼中寫Route就可以調用Illuminate\Support\Facades\Route呢?這個就是別名的用途了,很多門面都有自己的別名,這樣我們就不必在代碼里面寫use Illuminate\Support\Facades\Route,而是可以直接用Route了。 # 別名Aliases ??為什么我們可以在laravel中全局用Route,而不需要使用use Illuminate\Support\Facades\Route?其實奧秘在于一個PHP函數:[class_alias](http://www.php.net/manual/zh/function.class-alias.php),它可以為任何類創建別名。laravel在啟動的時候為各個門面類調用了class_alias函數,因此不必直接用類名,直接用別名即可。在config文件夾的app文件里面存放著門面與類名的映射: ~~~ 'aliases' => [ 'App' => Illuminate\Support\Facades\App::class, 'Artisan' => Illuminate\Support\Facades\Artisan::class, 'Auth' => Illuminate\Support\Facades\Auth::class, ... ] ~~~ 下面我們來看看laravel是如何為門面類創建別名的。 # 啟動別名Aliases服務 ??說到laravel的啟動,我們離不開index.php: ~~~ require __DIR__.'/../bootstrap/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); ... ~~~ ??第一句就是我們前面說的composer的自動加載,接下來第二句獲取laravel核心的Ioc容器,第三句“制造”出Http請求的內核,第四句是我們這里的關鍵,這句牽扯很大,laravel里面所有功能服務的注冊加載,乃至Http請求的構造與傳遞都是這一句的功勞。 ~~~ $request = Illuminate\Http\Request::capture() ~~~ 這句是laravel通過全局$_SERVER數組構造一個Http請求的語句,接下來會調用Http的內核函數handle: ~~~ public function handle($request) { try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); $response = $this->renderException($request, $e); } catch (Throwable $e) { $this->reportException($e = new FatalThrowableError($e)); $response = $this->renderException($request, $e); } event(new Events\RequestHandled($request, $response)); return $response; } ~~~ 在handle函數方法中enableHttpMethodParameterOverride函數是允許在表單中使用delete、put等類型的請求。我們接著看sendRequestThroughRouter: ~~~ protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } ~~~ 前兩句是在laravel的Ioc容器設置request請求的對象實例,Facade中清楚request的緩存實例。bootstrap: ~~~ public function bootstrap() { if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); } } protected $bootstrappers = [ \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, \Illuminate\Foundation\Bootstrap\LoadConfiguration::class, \Illuminate\Foundation\Bootstrap\HandleExceptions::class, \Illuminate\Foundation\Bootstrap\RegisterFacades::class, \Illuminate\Foundation\Bootstrap\RegisterProviders::class, \Illuminate\Foundation\Bootstrap\BootProviders::class, ]; ~~~ ?$bootstrappers是Http內核里專門用于啟動的組件,bootstrap函數中調用Ioc容器的bootstrapWith函數來創建這些組件并利用組件進行啟動服務。app->bootstrapWith: ~~~ public function bootstrapWith(array $bootstrappers) { $this->hasBeenBootstrapped = true; foreach ($bootstrappers as $bootstrapper) { $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]); $this->make($bootstrapper)->bootstrap($this); $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]); } } ~~~ 可以看到bootstrapWith函數也就是利用Ioc容器創建各個啟動服務的實例后,回調啟動自己的函數bootstrap,在這里我們只看我們Facade的啟動組件 ~~~ \Illuminate\Foundation\Bootstrap\RegisterFacades::class ~~~ RegisterFacades的bootstrap函數: ~~~ class RegisterFacades { public function bootstrap(Application $app) { Facade::clearResolvedInstances(); Facade::setFacadeApplication($app); AliasLoader::getInstance($app->make('config')->get('app.aliases', [])) ->register(); } } ~~~ 可以看出來,bootstrap做了一下幾件事: 清除了Facade中的緩存 設置Facade的Ioc容器 獲得我們前面講的config文件夾里面app文件aliases別名映射數組 使用aliases實例化初始化AliasLoader 調用AliasLoader->register() ~~~ public function register() { if (! $this->registered) { $this->prependToLoaderStack(); $this->registered = true; } } protected function prependToLoaderStack() { spl_autoload_register([$this, 'load'], true, true); } ~~~ ??我們可以看出,別名服務的啟動關鍵就是這個spl_autoload_register,這個函數我們應該很熟悉了,在自動加載中這個函數用于解析命名空間,在這里用于解析別名的真正類名。 # 別名Aliases服務 ??我們首先來看看被注冊到spl_autoload_register的函數,load: ~~~ public function load($alias) { if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) { $this->loadFacade($alias); return true; } if (isset($this->aliases[$alias])) { return class_alias($this->aliases[$alias], $alias); } } ~~~ ??這個函數的下面很好理解,就是class_alias利用別名映射數組將別名映射到真正的門面類中去,但是上面這個是什么呢?實際上,這個是laravel5.4版本新出的功能叫做實時門面服務。 # 實時門面服務 ??其實門面功能已經很簡單了,我們只需要定義一個類繼承Facade即可,但是laravel5.4打算更近一步——自動生成門面子類,這就是實時門面。 ??實時門面怎么用?看下面的例子: ~~~ amespace App\Services; class PaymentGateway { protected $tax; public function __construct(TaxCalculator $tax) { $this->tax = $tax; } } ~~~ 這是一個自定義的類,如果我們想要為這個類定義一個門面,在laravel5.4我們可以這么做: ~~~ use Facades\ { App\Services\PaymentGateway }; Route::get('/pay/{amount}', function ($amount) { PaymentGateway::pay($amount); }); ~~~ ??當然如果你愿意,你還可以在alias數組為門面添加一個別名映射”PaymentGateway” => “use Facades\App\Services\PaymentGateway”,這樣就不用寫這么長的名字了。 ??那么這么做的原理是什么呢?我們接著看源碼 ~~~ protected static $facadeNamespace = 'Facades\\'; if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) { $this->loadFacade($alias); return true; } ~~~ ??如果命名空間是以Facades\開頭的,那么就會調用實時門面的功能,調用loadFacade函數: ~~~ protected function loadFacade($alias) { tap($this->ensureFacadeExists($alias), function ($path) { require $path; }); } ~~~ ??[tap](https://segmentfault.com/a/1190000008447747)是laravel的全局幫助函數,ensureFacadeExists函數負責自動生成門面類,loadFacade負責加載門面類: ~~~ protected function ensureFacadeExists($alias) { if (file_exists($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) { return $path; } file_put_contents($path, $this->formatFacadeStub( $alias, file_get_contents(__DIR__.'/stubs/facade.stub') )); return $path; } ~~~ 可以看出來,laravel框架生成的門面類會放到stroge/framework/cache/文件夾下,名字以facade開頭,以命名空間的哈希結尾。如果存在這個文件就會返回,否則就要利用file_put_contents生成這個文件,formatFacadeStub: ~~~ protected function formatFacadeStub($alias, $stub) { $replacements = [ str_replace('/', '\\', dirname(str_replace('\\', '/', $alias))), class_basename($alias), substr($alias, strlen(static::$facadeNamespace)), ]; return str_replace( ['DummyNamespace', 'DummyClass', 'DummyTarget'], $replacements, $stub ); } ~~~ 簡單的說,對于Facades\App\Services\PaymentGateway,$replacements第一項是門面命名空間,將Facades\App\Services\PaymentGateway轉為Facades/App/Services/PaymentGateway,取前面Facades/App/Services/,再轉為命名空間Facades\App\Services\;第二項是門面類名,PaymentGateway;第三項是門面類的服務對象,App\Services\PaymentGateway,用這些來替換門面的模板文件: ~~~ <?php namespace DummyNamespace; use Illuminate\Support\Facades\Facade; /** * @see \DummyTarget */ class DummyClass extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'DummyTarget'; } } ~~~ 替換后的文件是: ~~~ <?php namespace Facades\App\Services\; use Illuminate\Support\Facades\Facade; /** * @see \DummyTarget */ class PaymentGateway extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'App\Services\PaymentGateway'; } } ~~~ 就是這么簡單!!!
                  <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>

                              哎呀哎呀视频在线观看