中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Laravel的生命周期實例分析

發布時間:2022-04-25 13:39:54 來源:億速云 閱讀:148 作者:zzz 欄目:編程語言

本篇內容主要講解“Laravel的生命周期實例分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Laravel的生命周期實例分析”吧!

Laravel的生命周期 A

世間萬物皆有生命周期,當我們使用任何工具時都需要理解它的工作原理,那么用起來就會得心應手,應用開發也是如此。理解了它的原理,那么使用起來就會游刃有余。

在了解 Laravel 的生命周期前,我們先回顧一下PHP 的生命周期。

PHP 的運行模式

PHP兩種運行模式是WEB模式、CLI模式。

當我們在終端敲入php這個命令的時候,使用的是CLI模式。

當使用Nginx或者別web服務器作為宿主處理一個到來的請求時,使用的是WEB模式。

PHP 的生命周期

當我們請求一個php文件時,PHP 為了完成這次請求,會發生5個階段的生命周期切換:

  • 1 模塊初始化(MINIT),即調用 php.ini 中指明的擴展的初始化函數進行初始化工作,如 mysql 擴展。

  • 2 請求初始化(RINIT),即初始化為執行本次腳本所需要的變量名稱和變量值內容的符號表,如 $_SESSION變量。

  • 3 執行該PHP腳本。

  • 4 請求處理完成(Request Shutdown),按順序調用各個模塊的 RSHUTDOWN 方法,對每個變量調用 unset 函數,如 unset $_SESSION 變量。

  • 5 關閉模塊(Module Shutdown) , PHP調用每個擴展的 MSHUTDOWN 方法,這是各個模塊最后一次釋放內存的機會。這意味著沒有下一個請求了。

WEB模式和CLI(命令行)模式很相似,區別是:

CLI 模式會在每次腳本執行經歷完整的5個周期,因為你腳本執行完不會有下一個請求;

WEB模式為了應對并發,可能采用多線程,因此生命周期1和5有可能只執行一次,下次請求到來時重復2-4的生命周期,這樣就節省了系統模塊初始化所帶來的開銷。

可以看出PHP生命周期是很對稱的。說了這么多,就是為了定位Laravel運行在哪里,沒錯,Laravel僅僅運行再 第三個階段:

作用

理解這些,你就可以優化你的 Laravel 代碼,可以更加深入的了解 Laravel 的singleton(單例)。

至少你知道了,每一次請求結束,PHP 的變量都會 unset,Laravel 的 singleton 只是在某一次請求過程中的singleton;

你在 Laravel 中的靜態變量也不能在多個請求之間共享,因為每一次請求結束都會 unset。

理解這些概念,是寫高質量代碼的第一步,也是最關鍵的一步。因此記住,PHP是一種腳本語言,所有的變量只會在這一次請求中生效,下次請求之時已被重置,而不像Java靜態變量擁有全局作用。

Laravel 的生命周期

概述

Laravel 的生命周期從public\index.php開始,從public\index.php結束。

請求過程

下面是 public\index.php的全部源碼,更具體來說可以分為四步:

<?php
define('LARAVEL_START', microtime(true));

require __DIR__.'/../vendor/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()
);

$response->send();

$kernel->terminate($request, $response);

以下是四步詳細的解釋是:composer自動加載需要的類

  • 1 文件載入composer生成的自動加載設置,包括所有你 composer require的依賴。

  • 2 生成容器 Container,Application實例,并向容器注冊核心組件(HttpKernel,ConsoleKernel ,ExceptionHandler)(對應代碼2,容器很重要,后面詳細講解)。

  • 3 處理請求,生成并發送響應(對應代碼3,毫不夸張的說,你99%的代碼都運行在這個小小的handle 方法里面)。

  • 4 請求結束,進行回調(對應代碼4,還記得可終止中間件嗎?沒錯,就是在這里回調的)。

Laravel 的請求步驟

第一步:注冊加載composer自動生成的class loader
就是加載初始化第三方依賴。

第二步:生成容器 Container
并向容器注冊核心組件,是從 bootstrap/app.php 腳本獲取 Laravel 應用實例,

第三步:這一步是重點,處理請求,并生成發送響應。
請求被發送到 HTTP 內核或 Console 內核,這取決于進入應用的請求類型。

取決于是通過瀏覽器請求還是通過控制臺請求。這里我們主要是通過瀏覽器請求。

HTTP 內核繼承自 Illuminate\Foundation\Http\Kernel 類,該類定義了一個 bootstrappers 數組,這個數組中的類在請求被執行前運行,這些 bootstrappers 配置了錯誤處理、日志、檢測應用環境以及其它在請求被處理前需要執行的任務。

protected $bootstrappers = [        //注冊系統環境配置 (.env)        'Illuminate\Foundation\Bootstrap\DetectEnvironment',        //注冊系統配置(config)        'Illuminate\Foundation\Bootstrap\LoadConfiguration',        //注冊日志配置        'Illuminate\Foundation\Bootstrap\ConfigureLogging',        //注冊異常處理        'Illuminate\Foundation\Bootstrap\HandleExceptions',        //注冊服務容器的門面,Facade 是個提供從容器訪問對象的類。        'Illuminate\Foundation\Bootstrap\RegisterFacades',        //注冊服務提供者        'Illuminate\Foundation\Bootstrap\RegisterProviders',        //注冊服務提供者 `boot`        'Illuminate\Foundation\Bootstrap\BootProviders',    ];

Laravel的生命周期 B

laravel/public/index.php

/**
 * laravel的啟動時間
 */
define('LARAVEL_START', microtime(true));

/**
 * 加載項目依賴。
 * 現代PHP依賴于Composer包管理器,入口文件通過引入由Composer包管理器。
 * 自動生成的類加載程序,可以輕松注冊并加載所依賴的第三方組件庫。
 */
require __DIR__.'/../vendor/autoload.php';

/**
 * 創建laravel應用實例。
 */
$app = require_once __DIR__.'/../bootstrap/app.php';

// 接受請求并響應
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

// 結束請求,進行回調
$response->send();

// 終止程序
$kernel->terminate($request, $response);

laravel/boostrap/app.php

# 第一部分:創建應用實例
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

# 第二部分:完成內核綁定
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

return $app;

laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php

class Kernel implements KernelContract
{
    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,                    // 注冊服務提供者boot
    ];

    // 處理請求
    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);
        }

        $this->app['events']->dispatch(
            new Events\RequestHandled($request, $response)
        );

        return $response;
    }

    protected function sendRequestThroughRouter($request)
    {
        # 一、將$request實例注冊到APP容器
        $this->app->instance('request', $request);

        # 二、清除之前的$request實例緩存
        Facade::clearResolvedInstance('request');

        # 三、啟動引導程序
        $this->bootstrap();

        # 四、發送請求
        return (new Pipeline($this->app)) //創建管道
                    ->send($request)      //發送請求
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)  //通過中間件
                    ->then($this->dispatchToRouter());  //分發到路由
    }

    # 啟動引導程序
    public function bootstrap()
    {
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }
    
    # 路由分發
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance('request', $request);

            return $this->router->dispatch($request);
        };
    }

    #  終止程序
    public function terminate($request, $response)
    {
        $this->terminateMiddleware($request, $response);

        $this->app->terminate();
    }

Laravel 服務容器模塊

簡介

服務容器是一個用于管理類依賴和執行依賴注入的強大工具。是整個框架的核心;

幾乎所有的服務容器綁定都是在服務提供者中完成。

框架調用分析

在框架直接生成服務容器的只有一處,在bootstrap/app.php,通過require引用會返回服務容器實例。通過require引用有兩處,一處是public/index.php,服務器訪問的入口;另一處是tests/CreatesApplication.php,是單元測試的入口;

如果想在項目各處中調用,可以調用$app = Illuminate\Container\Container::getInstance()或者全局幫助函數app()獲取服務容器實例(也就是Illuminate\Foundation/Application實例);

Illuminate\Foundation/Application是對Illuminate\Container\Container的又一層封裝;

Application初始化

那么實例化Illuminate\Foundation/Application時,做了什么呢?

第一步,設置應用的根目錄,并同時注冊核心目錄到服務容器中;核心的目錄有以下

  • path:目錄app的位置

  • path.base:項目根目錄的位置

  • path.lang:目錄resources/lang的位置

  • path.config:目錄config的位置

  • path.public:目錄public的位置

  • path.storage:目錄storage的位置

  • path.database:目錄database的位置

  • path.resources:目錄resources的位置

  • path.bootstrap:目錄bootstrap的位置

第二步,將當前Illuminate\Foundation/Application實例保存到$instance類變量,并同時綁定到服務容器作單例綁定,綁定名為appContainer::class

第三步,順序分別執行注冊Illuminate\Events\EventServiceProviderIlluminate\Log\LogServiceProviderIlluminate\Routing\RoutingServiceProvider三個服務提供者;

注冊服務提供者的順序如下:

  • 如果類變量$serviceProviders已經存在該服務提供者并且不需要強制重新注冊,則返回服務提供者實例$provider

  • 未注冊過當前服務提供者,則繼續執行以下;

  • 如果存在register方法,執行服務提供者的register方法;

  • 將當前服務提供者$provider實例保存到類變量$serviceProviders數組中,同時標記類變量$loadedProviders[get_class($provider)]的值為true

  • 判斷類變量$booted是否為true,如果是true,則執行服務提供者的boot方法;(類變量$booted應該是標志是否所有服務提供者均注冊,框架是否啟動)

第四步,注冊核心類別名;
比如\Illuminate\Foundation\Application::class\Illuminate\Contracts\Container\Container::class起別名為app

單元測試Application的bootstrap啟動分析

啟動代碼很簡潔,

Route::get('dev', 'Dev@index');

public function index()
{
     // require 初始化分析上面已經介紹了
    $app = require base_path('bootstrap/app.php');
    $kernel = $app->make('Illuminate\Contracts\Http\Kernel');
    
    dd($kernel);
}

構造函數主要干了一件事,注冊一個booted完成后的回調函數,函數執行的內容為“注冊 Schedule實例到服務提供者,同時加載用戶定義的Schedule任務清單”;

bootstrap方法的執行內容如下:

  1. 加載Illuminate/Foundation/Console/Kernel$bootstrappers變量數組中的類,執行它們的bootstrap方法;

  2. protected $bootstrappers = [
        // 加載 .env 文件
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        // 加載 config 目錄下的配置文件
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        // 自定義錯誤報告,錯誤處理方法及呈現
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        // 為 config/app.php 中的 aliases 數組注冊類別名
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        // 在服務容器中單例綁定一個 request 對象,控制臺命令會用到
        \Illuminate\Foundation\Bootstrap\SetRequestForConsole::class,
        // 注冊 config\app.php 中的 providers 服務提供者
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        // 項目啟動,執行每個 ServiceProvider 的 boot 方法,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];
  3. 加載延遲的服務提供者;

Http訪問Application的bootstrap啟動分析

啟動入口文件在public\index.php

$app = require_once __DIR__.'/../bootstrap/app.php';

// 實例化 Illuminate/Foundation/Http/Kernel 對象
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

// 中間件處理、業務邏輯處理
$response = $kernel->handle(
    // 根據 Symfony 的 request 對象封裝出 Illuminate\Http\Request
    $request = Illuminate\Http\Request::capture() 
);

$response->send();

// 執行所有中間件的 terminate 方法,執行 Application 中的 terminatingCallbacks 回調函數
$kernel->terminate($request, $response);

重要的類變量數組

aliases數組

維護 類與別名 的數組;鍵名為 類的全限定類名,鍵值為 數組,每一個元素都是該類的別名;

判斷指定類是否有別名:app()->isAlias($name)

獲取指定類的別名:app()->getAlias($abstract)

abstractAliases數組

維護 類與別名 的數組;鍵名為 別名,鍵值為 類的全限定類名;

instances數組

維護 類與實例的數組;鍵名為 類的全限定類名,鍵值為該類的實例;

移除綁定類:app()->forgetInstance($abstract);

移除所有綁定類:app()->forgetInstances();

bindings數組

通過 bind 方法實現 接口類與實現的綁定;

獲取bindings數組中的內容:app()->getBindings()

resolved數組

鍵名為 類的全限定類名,鍵值為布爾值類型(true表示已解析過,false表示未解析過);

with 數組

resolved過程中,會有一些參數;with數組就是參數棧,開始解析時將參數入棧,結束解析時參數出棧;

contextual數組

上下文綁定數組;第一維數組鍵名為 場合類(比如某個Controller類的類名),第二維數組鍵名為 抽象類(需要實現的接口類),鍵值為 Closure 或 某個具體類的類名;

tags數組

維護 標簽與類 的數組;鍵名是 標簽名,鍵值是 對應要綁定的類的名稱;

如果調用tagged方法,會將鍵值數組中的類都make出來,并以數組形式返回;

extenders數組

makeresolve出對象的時候,會執行

foreach ($this->getExtenders($abstract) as $extender) {    $object = $extender($object, $this);}

能對解析出來的對象進行修飾;

methodBindings數組

向容器綁定方法與及實現:app()->bindMethod($method, $callback)

判斷容器內是否有指定方法的實現:app()->hasMethodBinding($method)
執行方法的實現:app()->callMethodBinding($method, $instance)或者app()->call($method)

buildStack數組

調用build方法時維護的棧,棧中存放的是當前要new的類名;

reboundCallbacks數組

當調用rebound函數時,會觸發rebound中為此$abstract設置的回調函數;

注冊入口:app()->rebinding($abstract, Closure $callback);

serviceProviders數組

已在系統注冊的服務提供者ServiceProvider

數組內存放的是loadedProviders鍵名對應類的實例;

loadedProviders數組

系統已加載的ServiceProvider的集合;鍵名為ServiceProvider的全限定類名,鍵值為布爾值(true表示已加載,false表示未加載);

獲取延遲加載對象:app()->getLoadedProviders();

deferredServices數組

有些服務提供者是會延遲加載的;這時候會將這些服務提供者聲明的服務登錄在deferredServices數組,鍵名為延遲加載對象名 ,鍵值為該延遲加載對象所在的ServiceProvider

獲取延遲加載對象:app()->getDeferredServices();

bootingCallbacks數組

項目啟動前執行的回調函數;(項目啟動是在執行\Illuminate\Foundation\Bootstrap\BootProviders::class的時候)

注冊入口:app()->booting($callback);

bootedCallbacks數組

項目啟動后執行的回調函數;(項目啟動是在執行\Illuminate\Foundation\Bootstrap\BootProviders::class的時候)

注冊入口:app()->booted($callback);

resolvingCallbacks數組

解析時回調函數集合;鍵名為 類名, 鍵值為 回調函數數組,每一個元素都是回調函數;

注冊入口:app()->resolving($abstract, $callback);

afterResolvingCallbacks數組

解析后回調函數集合;鍵名為 類名, 鍵值為 回調函數數組,每一個元素都是回調函數;

注冊入口:app()->afterResolving($abstract, $callback);

globalResolvingCallbacks數組

全局解析時回調函數集合;每一次resolve方法調用時都會執行的回調函數集合;

注冊入口:app()->resolving($callback);

globalAfterResolvingCallbacks數組

全局解析后回調函數集合;每一次resolve方法調用后都會執行的回調函數集合;

注冊入口:app()->afterResolving($callback);

terminatingCallbacks數組

系統在返回response之后,會執行terminate方法,來做應用結束前的掃尾處理;

這個數組就是執行terminate方法時會執行的回調函數集合;

注冊入口:app()->terminating(Closure $callback);

常用方法的解析

bind方法

public function bind($abstract, $concrete = null, $shared = false)

第一個參數是要注冊的類名或接口名,第二個參數是返回類的實例的閉包(或類的實例類名),第三個參數是否是單例;

方法內部流程:

  1. unsetinstancesaliases 數組中鍵值為 $abstract 的元素;

  2. 如果 $concrete 值為 null ,將 $abstract 賦值給 $concrete

  3. 如果 $concrete 不是 Closure 對象,則封裝成閉包;

  4. $concrete$shared 通過 compact,添加進 bindings 數組,鍵名為 $abstract

  5. 判斷 $abstractresolvedinstances 數組中是否存在,如果存在,則執行第 6 步;

  6. 觸發 rebound回調函數;如果 reboundCallbacks 數組中注冊以 $abstract 為鍵名的回調函數,則執行這些回調函數;

涉及數組:instancesaliases(unset 操作)、bindings(add 操作)

singleton方法

單例綁定;

public function singleton($abstract, $concrete = null)    $this->bind($abstract, $concrete, true);}

涉及數組:instancesaliases(unset 操作)、bindings(add 操作)

bindIf方法

單例綁定;

public function bindIf($abstract, $concrete = null, $shared = false) {
    if (! $this->bound($abstract)) {
        $this->bind($abstract, $concrete, $shared);
    }
}

涉及數組:instancesaliases(unset 操作)、bindings(add 操作)

instance方法

綁定實例;

public function instance($abstract, $instance)

方法內部流程:

  1. 如果$abstractaliases數組中存在,則從abstractAliases中所有的值數組中移除該類;

  2. unsetaliases 數組中鍵名為 $abstract的元素;

  3. 賦值操作:$this->instances[$abstract] = $instance;

  4. 判斷 $abstractresolvedinstances 數組中是否存在,如果存在,則執行第 5 步;

  5. 觸發 rebound回調函數;如果 reboundCallbacks 數組中注冊以 $abstract 為鍵名的回調函數,則執行這些回調函數;

涉及數組:instances(add 操作)、aliasesabstractAliases(unset 操作)

make方法

public function make($abstract) {    return $this->resolve($abstract);}

alias

給類起別名;

public function alias($abstract, $alias) {
    $this->aliases[$alias] = $abstract;
    
    $this->abstractAliases[$abstract][] = $alias;
}

涉及數組:aliasesabstractAliases(add 操作)

laravel 的源代碼生命周期

第一步 Laravel 應用的所有請求入口都是 public/index.php 文件。打開 index.php 發現代碼也就幾行。

下面我們來講一下每一句代碼的作用是什么?

// 定義了laravel一個請求的開始時間
define('LARAVEL_START', microtime(true));

// composer自動加載機制
require __DIR__.'/../vendor/autoload.php';

// 這句話你就可以理解laravel,在最開始引入了一個ioc容器。
$app = require_once __DIR__.'/../bootstrap/app.php';

// 打開__DIR__.'/../bootstrap/app.php';你會發現這段代碼,綁定了Illuminate\Contracts\Http\Kernel::class,
// 這個你可以理解成之前我們所說的$ioc->bind();方法。
// $app->singleton(
//     Illuminate\Contracts\Http\Kernel::class,
//    App\Http\Kernel::class
// );

// 這個相當于我們創建了Kernel::class的服務提供者
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

// 獲取一個 Request ,返回一個 Response。以把該內核想象作一個代表整個應用的大黑盒子,輸入 HTTP 請求,返回 HTTP 響應。
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

// 就是把我們服務器的結果返回給瀏覽器。
$response->send(); 

// 這個就是執行我們比較耗時的請求,
$kernel->terminate($request, $response);

走到這里你會發現,是不是在我們學會了 ioc,服務提供者理解起來就比較簡單了。那 $middleware,服務提供者都是在哪個文件注冊運行的呢?

打開 App\Http\Kernel::class 這個文件,你會發現定義了一堆需要加載的 $middleware。這個 kernel 的主要方法還是在他的父類里面 Illuminate\Foundation\Http\Kernel 中。

打開 Illuminate\Foundation\Http\Kernel,你會發現定義了啟動時需要做的事呢?

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 就定義了我們的 RegisterFacades.class,RegisterProviders.class 這兩個類的意思就是要將我們在 app.config 中的 Providers,Facades 注入到我們的 Ioc 容器中。

到此,相信大家對“Laravel的生命周期實例分析”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

日喀则市| 岳池县| 湖州市| 宁乡县| 澄迈县| 洛隆县| 谷城县| 托里县| 越西县| 屏边| 富阳市| 孝感市| 石河子市| 木兰县| 广西| 塔城市| 抚顺县| 崇阳县| 京山县| 余庆县| 达州市| 富民县| 临沧市| 益阳市| 南丰县| 龙井市| 巨鹿县| 宣武区| 维西| 商水县| 廊坊市| 江口县| 闽清县| 阜城县| 胶州市| 策勒县| 明溪县| 鲁山县| 泽库县| 盐山县| 汾西县|