Laravel的核心概念

2021-03-02 PHP開源社區

        

                成功就是你被擊落到失望的深淵之後反彈得有多高。

                                                        

                                                             

幫助你練腦子的邏輯思維題:

1=5 2=15 3=215 4=2145 那麼5=?  

答案將會公布在下一期的文章裡 (聽說長得帥氣的都做出來了)

上一期的答案公布:



6種結果 
大、中、小:(2\30\68)(5\25\70)(8\20\72)(11\15\74)(14\10\76)(17\5\78) 

(你答對了嗎?

今天推送一首周杰倫的歌,你還記得當年初中的你是什麼樣子嗎?是什麼信念支撐你走到了今天呀?

IT短新聞時間:

當搭上機器學習,鍵盤也能通過打字習慣辨別男女,日前,一項新研究顯示,除了攝像頭之外,計算機還能通過打字習慣來判斷打字的人是男性還是女性,並且識別的準確率高達95.6%。

在獲取了監控數據之後,利用軟體對數據進行分析,如在鍵盤上按下兩個特定按鍵的間隔時間或按下某個按鍵的時長等,計算出各種打字特徵對判定性別的相對有效性。

分析結果顯示,不同性別按下「N」和「O」兩個鍵的間隔時間差別最大,其次是「M」和「O」的間隔時間。

接著,研究人員採用五套機器學習模型,對上述發現展開測試,並且五套模型判斷性別的準確率均超過了78%,其中最成功的模型準確率甚至超過了95%。

另外,研究人員正準備進一步進行測試,並從更多受試者中獲取更多數據,以擴充資料庫,提升識別準確率。

該研究團隊提出,通過按鍵規律判斷性別能夠在網絡跟蹤、身份竊取等案件調查中作為一種高性價比、且不易察覺的調查手段。

PHP的生命周期

萬物皆有他的生命周期。熟悉Android的同學一定熟悉Android*經典的Activity的生命周期,Laravel 也不例外,Laravel應用程式也有自己的生命周期。Laravel是什麼?一個PHP框架。所以要想真正說清Laravel的生命周期,必須說清PHP的生命周期。原文參考這裡,這裡做個總結。

Php有兩種運行模式,WEB模式和CLI(命令行)模式。當我們在終端敲入php這個命令的時候,使用的是CLI模式;當使用Nginx或者別web伺服器作為宿主處理一個到來的請求時,會調用Php運行,此時使用的是WEB模式。當我們請求一個Php文件時,比如Laravel 的public\index.php文件時,Php 為了完成這次請求,會發生5個階段的生命周期切換:

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

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

執行該PHP腳本。

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

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

WEB模式和CLI(命令行)模式很相似,區別是:CLI 模式會在每次腳本執行經歷完整的5個周期,因為你腳本執行完不會有下一個請求;而WEB模式為了應對並發,可能採用多線程,因此生命周期1和5有可能只執行一次,下次請求到來時重複2-4的生命周期,這樣就節省了系統模塊初始化所帶來的開銷。

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

知道這些有什麼用?你可以優化你的Laravel代碼,可以更加深入的了解Larave的singleton(單例)。至少你知道了,每一次請求結束,Php的變量都會unset,Laravel的singleton只是在某一次請求過程中的singleton;你在Laravel 中的靜態變量也不能在多個請求之間共享,因為每一次請求結束都會unset。理解這些概念,是寫高質量代碼的第一步,也是*關鍵的一步。因此記住,Php是一種腳本語言,所有的變量只會在這一次請求中生效,下次請求之時已被重置,而不像Java靜態變量擁有全局作用。

好了,開始Laravel的生命周期。

Laravel的生命周期概述

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

注意:以下幾圖箭頭均代表Request流向

這麼說有點草率,但事實確實如此。下面是public\index.php的全部源碼(Laravel源碼的注釋是*好的Laravel文檔),更具體來說可以分為四步:

1. require __DIR__.'/../bootstrap/autoload.php';2. $app = require_once __DIR__.'/../bootstrap/app.php';   $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);   3. $response = $kernel->handle(    $request = Illuminate\Http\Request::capture()   );   $response->send();4. $kernel->terminate($request, $response);

這四步詳細的解釋是:

1.註冊加載composer自動生成的class loader,包括所有你composer require的依賴(對應代碼1).
2.生成容器Container,Application實例,並向容器註冊核心組件(HttpKernel,ConsoleKernel,ExceptionHandler)(對應代碼2,容器很重要,後面詳細講解)。
3.處理請求,生成並發送響應(對應代碼3,毫不誇張的說,你99%的代碼都運行在這個小小的handle方法裡面)。
4.請求結束,進行回調(對應代碼4,還記得可終止中間件嗎?沒錯,就是在這裡回調的)。

啟動Laravel基礎服務

我們不妨再詳細一點:

第一步註冊加載composer自動生成的class loader就是加載初始化第三方依賴,不屬於Laravel核心,到此為止。

第二步生成容器Container,並向容器註冊核心組件,這裡牽涉到了容器Container和合同Contracts,這是Laravel的重點,下面將詳細講解。

重點是第三步處理請求,生成並發送響應。
首先Laravel框架捕獲到用戶發到public\index.php的請求,生成Illuminate\Http\Request實例,傳遞給這個小小的handle方法。在方法內部,將該$request實例綁定到第二步生成的$app容器上。讓後在該請求真正處理之前,調用bootstrap方法,進行必要的加載和註冊,如檢測環境,加載配置,註冊Facades(假象),註冊服務提供者,啟動服務提供者等等。這是一個啟動數組,具體在Illuminate\Foundation\Http\Kernel中,包括:

protected $bootstrappers = ['Illuminate\Foundation\Bootstrap\DetectEnvironment','Illuminate\Foundation\Bootstrap\LoadConfiguration', 'Illuminate\Foundation\Bootstrap\ConfigureLogging','Illuminate\Foundation\Bootstrap\HandleExceptions','Illuminate\Foundation\Bootstrap\RegisterFacades','Illuminate\Foundation\Bootstrap\RegisterProviders','Illuminate\Foundation\Bootstrap\BootProviders', ];

看類名知意,Laravel是按順序遍歷執行註冊這些基礎服務的,注意順序:Facades先於ServiceProviders,Facades也是重點,後面說,這裡簡單提一下,註冊Facades就是註冊config\app.php中的aliases 數組,你使用的很多類,如Auth,Cache,DB等等都是Facades;而ServiceProviders的register方法永遠先於boot方法執行,以免產生boot方法依賴某個實例而該實例還未註冊的現象。

所以,你可以在ServiceProviders的register方法中使用任何Facades,在ServiceProviders的boot方法中使用任何register方法中註冊的實例或者Facades,這樣絕不會產生依賴某個類而未註冊的現象。

將請求傳遞給路由

注意到目前為止,Laravel 還沒有執行到你所寫的主要代碼(ServiceProviders中的除外),因為還沒有將請求傳遞給路由。

在Laravel基礎的服務啟動之後,就要把請求傳遞給路由了。傳遞給路由是通過Pipeline(另開篇章講解)來傳遞的,但是Pipeline有一堵牆,在傳遞給路由之前所有請求都要經過,這堵牆定義在app\Http\Kernel.php中的$middleware數組中,沒錯就是中間件,默認只有一個CheckForMaintenanceMode中間件,用來檢測你的網站是否暫時關閉。這是一個全局中間件,所有請求都要經過,你也可以添加自己的全局中間件。

然後遍歷所有註冊的路由,找到*先符合的第一個路由,經過它的路由中間件,進入到控制器或者閉包函數,執行你的具體邏輯代碼。

所以,在請求到達你寫的代碼之前,Laravel已經做了大量工作,請求也經過了千難萬險,那些不符合或者惡意的的請求已被Laravel隔離在外。

服務容器

服務容器就是一個普通的容器,用來裝類的實例,然後在需要的時候再取出來。用更專業的術語來說是服務容器實現了控制反轉(Inversion of Control,縮寫為IoC),意思是正常情況下類A需要一個類B的時候,我們需要自己去new類B,意味著我們必須知道類B的更多細節,比如構造函數,隨著項目的複雜性增大,這種依賴是毀滅性的。控制反轉的意思就是,將類A主動獲取類B的過程顛倒過來變成被動,類A只需要聲明它需要什麼,然後由容器提供。

這樣做的好處是,類A不依賴於類B的實現,這樣在一定程度上解決了耦合問題。

在Laravel的服務容器中,為了實現控制反轉,可以有以下兩種:

依賴注入(Dependency Injection)。

綁定。

依賴注入

依賴注入是一種類型提示,舉官網的例子:

class UserController extends Controller{    /**     * The user repository implementation.     *     * @var UserRepository     */    protected $users;    /**     * Create a new controller instance.     *     * @param  UserRepository  $users     * @return void     */    public function __construct(UserRepository $users)    {        $this->users = $users;    }    /**     * Show the profile for the given user.     *     * @param  int  $id     * @return Response     */    public function show($id)    {        $user = $this->users->find($id);                return view('user.profile', ['user' => $user]);    }}

這裡UserController需要一個UserRepository實例,我們只需在構造方法中聲明我們需要的類型,容器在實例化UserController時會自動生成UserRepository的實例(或者實現類,因為UserRepository可以為接口),而不用主動去獲取UserRepository的實例,這樣也就避免了了解UserRepository的更多細節,也不用解決UserRepository所產生的依賴,我們所做的僅僅是聲明我們所需要的類型,所有的依賴問題都交給容器去解決。(Xblog使用了Repository的是設計模式,大家可以參考)

綁定

綁定操作一般在ServiceProviders中的register方法中,*基本的綁定是容器的bind方法,它接受一個類的別名或者全名和一個閉包來獲取實例:

$this->app->bind('XblogConfig', function ($app) {    return new MapRepository();});

還有一個singleton方法,和bind寫法沒什麼區別。你也可以綁定一個已經存在的對象到容器中,上文中提到的request實例就是通過這種方法綁定到容器的:$this->app->instance('request', $request);。綁定之後,我們可以通過一下幾種方式來獲取綁定實例:

1.  app('XblogConfig');2.  app()->make('XblogConfig');3.  app()['XblogConfig'];4.  resolve('XblogConfig');

以上四種方法均會返回獲得MapRepository的實例,唯一的區別是,在一次請求的生命周期中,bind方法的閉包會在每一次調用以上四種方法時執行,singleton方法的閉包只會執行一次。在使用中,如果每一個類要獲的不同的實例,或者需要「個性化」的實例時,這時我們需要用bind方法以免這次的使用對下次的使用造成影響;如果實例化一個類比較耗時或者類的方法不依賴該生成的上下文,那麼我們可以使用singleton方法綁定。singleton方法綁定的好處就是,如果在一次請求中我們多次使用某個類,那麼只生成該類的一個實例將節省時間和空間。

 

你也可以綁定接口與實現,例如:

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

上文講述的Laravel的生命周期的第二步,Laravel默認(在bootstrap\app.php文件中)綁定了Illuminate\Contracts\Http\Kernel,Illuminate\Contracts\Console\Kernel,Illuminate\Contracts\Debug\ExceptionHandler接口的實現類,這些是實現類框架的默認自帶的。但是你仍然可以自己去實現。

還有一種上下文綁定,就是相同的接口,在不同的類中可以自動獲取不同的實現,例如:

$this->app->when(PhotoController::class)          ->needs(Filesystem::class)          ->give(function () {                        return Storage::disk('local');          });$this->app->when(VideoController::class)          ->needs(Filesystem::class)          ->give(function () {                        return Storage::disk('s3');          });

上述表明,同樣的接口Filesystem,使用依賴注入時,在PhotoController中獲取的是local存儲而在VideoController中獲取的是s3存儲。

Contracts & Facades(合同&假象)

Laravel 還有一個強大之處是,比如你只需在配置文件中指明你需要的緩存驅動(redis,memcached,file.),Laravel 就自動辦你切換到這種驅動,而不需要你針對某種驅動更改邏輯和代碼。Why? 很簡單,Laravel定義了一系列Contracts(翻譯:合同),本質上是一系列PHP接口,一系列的標準,用來解耦具體需求對實現的依賴關係。其實真正強大的公司是制定標準的公司,程序也是如此,好的標準(接口)尤為重要。當程序變得越來大,這種通過合同或者接口來解耦所帶來的可擴展性和可維護性是無可比擬的。

上圖不使用Contracts的情況下,對於一種邏輯,我們只能得到一種結果(方塊),如果變更需求,意味著我們必須重構代碼和邏輯。但是在使用Contracts的情況下,我們只需要按照接口寫好邏輯,然後提供不同的實現,就可以在不改動代碼邏輯的情況下獲得更加多態的結果。

這麼說有點抽象,舉一個真實的例子。在完成Xblog的初期,我使用了緩存,所以導致Repository中充滿了和cache相關的方法:remember,flush,forget等等。後來國外網友反映,簡單的博客並不一定需要緩存。所以我決定把它變成可選,但因為代碼中充滿和cache相關的方法,實現起來並不是很容易。於是想起Laravel的重要概念Contracts。於是,我把與緩存有關的方法抽象出來形成一個Contracts:XblogCache,實際操作只與Contracts有關,這樣問題就得到了解決,而幾乎沒有改變原有的邏輯。XblogCache的代碼如下(源碼點擊這裡):

namespace App\Contracts;use Closure;interface XblogCache{    public function setTag($tag);     public function setTime($time_in_minute);    public function remember($key, Closure $entity, $tag = null);    public function forget($key, $tag = null);     public function clearCache($tag = null);   public function clearAllCache();}

然後,我又完成了兩個實現類:Cacheable和NoCache:

實現具體緩存。


不緩存

然後再利用容器的綁定,根據不同的配置,返回不同的實現(源碼):

public function register(){

$this->app->bind('XblogCache', function ($app)

{if (config('cache.enable') == 'true')

{return new Cacheable(); } else { return new NoCache(); } });}

這樣,就實現了緩存的切換而不需要更改你的具體邏輯代碼。當然依靠接口而不依靠具體實現的好處不僅僅這些。實際上,Laravel所有的核心服務都是實現了某個Contracts接口(都在Illuminate\Contracts\文件夾下面),而不是依賴具體的實現,所以完全可以在不改動框架的前提下,使用自己的代碼改變Laravel框架核心服務的實現方式。

說一說Facades。在我們學習了容器的概念後,Facades就變得十分簡單了。在我們把類的實例綁定到容器的時候相當於給類起了個別名,然後覆蓋Facade的靜態方法getFacadeAccessor並返回你的別名,然後你就可以使用你自己的Facade的靜態方法來調用你綁定類的動態方法了。其實Facade類利用了__callStatic() 這個魔術方法來延遲調用容器中的對象的方法,這裡不過多講解,你只需要知道Facade實現了將對它調用的靜態方法映射到綁定類的動態方法上,這樣你就可以使用簡單類名調用而不需要記住長長的類名。這也是Facades的中文翻譯為假象的原因。

總結

Laravel強大之處不僅僅在於它給你提供了一系列腳手架,比如超級好用的ORM,基於Carbon的時間處理,以及文件存儲等等功能。但是Laravel的核心非常非常簡單:利用容器和抽象解耦,實現高擴展性。容器和抽象是所有大型框架必須解決的問題,像Java的Spring,Android的Dagger2等等都是圍繞這幾個問題的。所以本質上講,Laravel之所以強大出名,是因為它的設計,思想,可擴展性。而Laravel的好用功能只是官方基於這些核心提供的腳手架,你同樣也可以很輕鬆的添加自己的腳手架。

所以不要覺得Laravel強大是因為他提供的很多功能,而是它的設計模式和思想。

理解Laravel生命周期和請求的生命周期概念。

所有的靜態變量和單例,在下一個請求到來時都會重新初始化。

將耗時的類或者頻繁使用的類用singleton綁定。

將變化選項的抽象為Contracts,依賴接口不依賴具體實現。

善於利用Laravel提供的容器。

六星教育公開課正式開課啦!想要繼續學習的同學,記得晚上來聽課哦,有珍貴視頻贈送。


今晚課程預告:(參與途徑點擊閱讀原文,登錄QQ免費報名即可)

課題:

神秘(11年高級架構師講解)


知識點:

神秘(11年高級架構師講解)


講師:

11年高級雲平臺架構師新年第一次講公開課,課題值得期待哦





閱讀原文登錄自己的QQ號,點擊立即報名,即可免費跟著我們學習從PHP基礎到微信開發、支付接口、tp5、laravel項目實戰、swoole擴展、redis高並發、隊列任務等技術,周一到周六,晚上八點半不見不散! 

相關焦點

  • 創建並運行一個新的 Laravel 項目
    使用 Laravel 安裝器安裝安裝 Laravel 安裝器很簡單,在命令行執行以下命令即可(如果已經安裝過,會自動進行更新):composer global require laravel/installer 安裝完成後,後續就可以通過 laravel new [項目名稱] 來創建新的 Laravel 項目了:laravel
  • Laravel 5.6 中文文檔目錄
    本篇是laravel5.6中文文檔的目錄。快速入門安裝配置目錄結構HomesteadValet部署核心概念
  • Laravel學習:服務容器綁定與解析
    $app = new Illuminate\Foundation\Application(    dirname(__DIR__));  這個文件在每一次請求到達laravel框架都會執行,所創建的$app即是laravel框架的應用程式實例,它在整個請求生命周期都是唯一的。
  • 安裝laravel,最靠譜的方法,利用composer安裝
    laravel框架是一款應用於web的php框架,這個框架因其非常優雅,簡潔方便使用而備受眾多開發者所使用,那麼今天我們就來介紹一下laravel這框框架在windows平臺怎樣進行安裝。二、安裝laravel安裝好composer之後,lavarel安裝就變得比較簡單了,在命令行中進入我們本地的項目文件夾,然後輸入命令composer create-project --prefer-dist laravel/laravel,按下回車鍵,就開始安裝laravel,安裝過程需要等待一些時間,安裝完成會有相應的提示。
  • Laravel5.4 Vuejs編譯失敗的問題處理
    /resources/assets/js/app.js 17:26-61', showStack: false, showProperties: true, plugin: 'webpack-stream', __safety: { toString: [Function: bound ] } }從拋出的錯誤我們可以看到,是未引入 'laravel-elixir-vue
  • laravel 基礎面試題-偏交談-2020-12-25-laravel-命令行工具artisan與tinker
    laravel 基礎面試題 - 偏交談 - 2020-12-25-laravel - 命令行工具 artisan 與 tinker首先了解一下,
  • laravel框架實戰,資料庫的增刪改查,facade的使用
    laravel框架實戰,資料庫的增刪改查,facade的使用!在上一篇的文章中,我們學習了如何讓在laravel中創建視圖和模型的操作,今天我們將來學習如何在laravel中對資料庫進行增刪改查,通過facade來實現,具體操作如下:前提:首先我們需要在laravel中配置好資料庫,進入laravel的目錄,找到.env這個文件,在裡面對資料庫的主機名,資料庫名,用戶名,密碼進行配置,配置成功之後,我們才能夠對資料庫進行設置
  • laravel應用 之多個路由文件配置
    很多php開發者或者新近開發者,在一開始使用或學習laravel框架的時候,肯定會默認把所有的路由寫在 route/web.php中。然而這樣會有一個問題,就是那麼多的路由寫在這樣一個文件中,會顯得很多很冗雜,當然了小編一開始也是這樣寫的,不過經過多方查找和實驗,小編無意間看到 laravel 的路由還可以這樣配置。
  • 特務Q:laravel與swoole優雅的結合
    laravel與swoole作為當下各自兩個方向的金字塔頂端的工程,有點誇大,仁者見仁。它們好比倚天劍屠龍刀,我們期望它們的結合,誕生一門強大的功法。讓我們可以更方便開發,下面來說說它們如何優雅的結合。你需要在你伺服器上面安裝php的swoole擴展。
  • Laravel 自定義 SQL 查詢參數綁定
    引言 laravel使用模型進行資料庫操作時,並不是所有的欄位或者關聯關係 都能滿足查詢需求,有時候會有一些MySQL的函數計算等功能放在資料庫 層面執行。
  • 創建Laravel自定義Helper輔助方法
    { "name": "laravel/framework", "description": "The Laravel Framework.", "keywords": ["framework", "laravel"], "license": "MIT", "homepage": "https://laravel.com", "support": { "issues": "https://github.com/laravel/framework/issues",
  • 完結篇:91 個常見的 Laravel 面試題和答案(三)
    51) 解釋一下 cookies 的概念Cookies 是從特定網站發送的小文件,並且在用戶瀏覽時由用戶的瀏覽器存儲在 PC 上。 52) 哪個文件用於與資料庫建立連接?要創建與資料庫的連接,可以使用.env 文件。
  • laravel高性能地從mysql資料庫中隨機獲取n條數據
    laravel如何高性能地從mysql資料庫中隨機獲取n條數據,有時候我們常常會需要從資料庫隨機獲取數據,比如:給工作人員隨機分配10個訂單,隨機從資料庫中隨機抽查100個用戶;這樣我們就需要隨機從資料庫獲取數據。
  • VSCode的Laravel語法提示插件
    了解更多您可以通過在 Visual Studio 的插件市場中搜索 laravel intellisense 來下載此插件。原始碼可在GitHub上的 simoebenhida/laravel-intellisense上找到。
  • Laravel 5.4 正式發布,PHP 開發框架 - OSCHINA - 中文開源技術...
    $invoices->each->pay();@milkmeowo 同學對此功能做了詳細的介紹,請前往查看 https://laravel-china.org更多新功能以下是不完全列表新增輔助函數 retry新增輔助函數 array_wrap新增默認 503 頁面整個核心加載類時重構為 ::class 調用增加密碼重置的路由命名支持 PhpRedis增加 IPv4 和 IPv6 驗證date_format 的驗證現在更加準確
  • PHP(Laravel)用戶註冊、登錄和退出
    那麼這些功能,在 laravel 中實現,需要多久呢?是一星期、一天、還是一小時呢?正確的答案是1秒鐘。打開命令行,輸入php artisan make:auth出現有一個提示,說我們布局模板已經有了,是否還要重新創建一個?
  • Laravel-基礎「程式設計師培養之路第五十三天」
    laravel第一節 安裝官方手冊推薦我們使用Laravel Homstead虛擬機,我們可以著手去配置該虛擬機,若是初學者的話,建議先使用本地環境wamp即可。1.安裝 Composer下載地址:github(點擊 Composer-Setup.exe 按鈕)3.下載 Laravelcomposer global require "laravel/installer"第二節 配置在使用前,要確保以下幾個內容的配置是正確的,配置有問題會影響我們的項目開發。1.
  • 3分鐘短文:說說Laravel通用緩存Cache的使用技巧
    代碼時間laravel在設計上做了很巧妙的構思,將緩存相關的類,設計得有插接件的感覺。 所以,你看到Session Cache Cookie 這些緩存數據類,基本上除了底層的驅動, 數據結構,過期特性等等,都集成了系統數組類Arr的操作方法。所以上述三種緩存在 操作方法上有很多相同之處。
  • 3分鐘短文:Laravel ORM 模型用法綱要
    引言 前兩期為了說明laravel框架提供的資料庫操作能力,直接使用DB門面操作, 而沒有引入更為強大的eloquent orm功能。從本期開始,我們就分次把 eloquent的一些簡要知識點,為大家提煉演示一下。主要以代碼為主,配以簡要說明。
  • 在laravel中怎樣創建視圖和模型?
    在上一篇文章中,我們介紹了在laravel中如何創建控制器,實現了MVC模型三部曲的第一步,今天我們來學習第二步和第三步——創建視圖和模型。一、模型的創建在MVC中,模型一般都是用來做數據處理的,在laravel中模型是直接放在App目錄下的,所以在模型中,命名空間就直接這樣寫:namespace App;在APP目錄下,我們直接新建一個php文件,模型的命名直接寫:名字+「.php」;例如: