Laravel 是優雅的 PHP Web 開發框架。具備高效、簡潔、富於表達力等優勢。採用 MVC 設計,是崇尚開發效率的全棧框架。是最受關注的 PHP 框架。php
因此 laravel 框架自己的設計思想我認爲值得咱們每一個 phper 去學習,這裏我經過一個手寫的簡易版框架去認識和了解 laravel 在應用層執行的過程。主要包含的內容有:容器、應用框架、內核、契約、入口文件、服務提供者、路由、請求、門面、控制器及輔助函數。文章結尾附上執行流程的圖片供你們參考。mysql
主要作了2件事情:laravel
①【綁定】對象、實例(共享實例)、接口(接口到實現)、閉包4個抽象和具體類到容器數組中
②【解析】已綁定的抽象(經過反射機制實現解析具體類中的依賴注入),即建立實例git
整一個框架裏面就圍繞這個容器提供依賴的,因此至關於一個心臟,也是 laravel 的一個心臟。這裏貼出源碼,後面就不貼了。github
<?php /** * [Description] * * @Author leeprince:2020-03-11 19:28 */ class Container { // 綁定到容器的數組,至關於 bindings 與 $aliases protected $bind = []; // 當前實例,單例建立 protected static $instance; // 綁定到容器的共享實例數組 protected $instances = []; // 參數覆蓋堆棧的數組 protected $with = []; /** * [綁定到容器數組] * * @Author leeprince:2020-03-11 19:36 * @param $abstract * @param $concrete */ public function bind($abstract, $concrete = null) { // 綁定自身 if (is_null($concrete)) { $concrete = $abstract; } $this->bind[$abstract] = $concrete; } /** * [校驗是否已綁定到容器] * * @Author leeprince:2020-03-12 01:15 */ public function has($abstract) { return isset($this->bind[$abstract]); } /** * [綁定到共享實例中] * * @Author leeprince:2020-03-12 01:17 * @param $abstract * @param $instance */ public function instance($abstract, $instance) { if (isset($this->bind[$abstract])) { unset($this->bind[$abstract]); } $this->instances[$abstract] = $instance; } /** * [獲取當前實例] * * @Author leeprince:2020-03-12 01:18 * @return Container */ public static function getInstance() { if (is_null(static::$instance)) { static::$instance = new static; } return static::$instance; } /** * [設置當前實例] * * @Author leeprince:2020-03-12 01:19 * @param $container * @return mixed */ public static function setInstance($container) { return static::$instance = $container; } /** * [建立實例] * * laravel 中是resolve 方法 * * @Author leeprince:2020-03-12 01:20 * @param $abstract * @return mixed * @throws Exception */ public function make($abstract, array $parameter = []) { // 判斷是否在共享實例中,有直接返回 if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } $this->with[] = $parameter; // 再優化版本:經過反射機制繼續遞歸解析具體類中的依賴注入:參考 IOC 的部分便可! if (isset($this->bind[$abstract])) { $concrete = $this->bind[$abstract]; // 若是具體實現是閉包那麼直接執行閉包,也沒必要綁定到共享實例中,由於閉包函數自己也不是實例。 // 下列有3中判斷是不是否一個閉包的方式。顯然第一種更加專業 if ($concrete instanceof Closure) { // if (is_object($concrete)) { // if (is_callable($concrete)) { return $concrete(); } /** * 有兩個版本 */ /** 版本1: 這是不考慮基於類的依賴注入的簡單版本 */ // return $this->instances[$abstract] = (empty($parameter))? new $concrete() : new $concrete(...$parameter); /** 版本2:經過反射機制考慮基於類的依賴注入的優化版本 */ // 經過反射類,反射出該具體的全部信息 $reflection = new ReflectionClass($concrete); if (! $reflection->isInstantiable()) { throw new Exception('反射後判斷該類沒法實例化'); } // 獲取構造函數 $constructor = $reflection->getConstructor(); if (is_null($constructor)) { $object = $reflection->newInstance(); } else { // 獲取構造函數參數 $depenen = $constructor->getParameters(); $instances = $this->getDependencies($depenen); $object = $reflection->newInstanceArgs($instances); } /** * 刪除參數覆蓋堆棧的數組的最後一條記錄。 * 由於這多是遞歸解析基於類的依賴注入, * 若是不刪除會致使在遞歸的上一步沒有獲取到當前解析中的參數覆蓋的參數數組 */ array_pop($this->with); return $this->instances[$abstract] = $object; } throw new Exception('沒有找到實例'.$abstract); } /** * [根據構造函數中的類型提示參數(依賴注入:基於類的依賴注入或者非基於類的原始依賴注入)中繼續實例化對象] * * @Author leeprince:2020-03-15 13:12 * @param array $depenen * @return array * @throws Exception */ public function getDependencies(array $dependencies) { $results = []; foreach ($dependencies as $key => $dependency) { // 肯定給定的依賴項是否具備參數替代 if ($this->hasParameterOverride($dependency)) { $results[] = $this->getParameterOverride($dependency); continue; } // 考慮構造函數的參數是否是基於類的依賴注入 $results[] = is_null($dependency->getClass()) ? $this->resolvePrimitive($dependency, $key) : $this->resolveClass($dependency); } return $results; } /** * [解析非基於類的原始依賴] * * @Author leeprince:2020-03-15 15:47 * @param ReflectionParameter $parameter * @param $key * @return mixed * @throws ReflectionException */ public function resolvePrimitive(ReflectionParameter $parameter, $key) { if ($parameter->isDefaultValueAvailable()) { return $parameter->getDefaultValue(); } return null; } /** * [解析容器中基於類的依賴] * * @Author leeprince:2020-03-15 15:29 * @param ReflectionParameter $parameter * @return mixed * @throws Exception */ public function resolveClass(ReflectionParameter $parameter) { return $this->make($parameter->getClass()->name, []); } /** * [肯定給定的依賴項是否具備參數替代。] * * @Author leeprince:2020-03-15 16:17 * @param $dependency * @return bool */ protected function hasParameterOverride(ReflectionParameter $dependency) { return array_key_exists( $dependency->name, $this->getLastParameterOverride() ); } /** * [獲取依賴項的參數覆蓋。] * * @Author leeprince:2020-03-15 16:18 * @param ReflectionParameter $dependency * @return mixed */ protected function getParameterOverride(ReflectionParameter $dependency) { return $this->getLastParameterOverride()[$dependency->name]; } /** * [獲取最後一個參數覆蓋] * * @Author leeprince:2020-03-15 16:23 * @return array|mixed */ protected function getLastParameterOverride() { return count($this->with) ? end($this->with) : []; } }
主要也是作2件事:在構造函數中註冊基本綁定和註冊基本服務sql
容器契約
內核契約
服務提供者契約
- 日誌契約數據庫
主要作了5件事,執行順序也是按照下面執行,固然能夠調整,只不過爲了更好理解我就這麼排了數組
這裏主要介紹的是服務的概念。設計思路與 laravel 關於服務提供者實現的不同,對這塊感興趣的朋友能夠看下源碼或者留言一塊兒討論。閉包
文件日誌服務類:實現契約接口的約束方法
mysql 日誌服務類:實現契約接口的約束方法架構
主要作了3件事
設置請求地址到請求對象的屬性中,並將請求對象返回
門面:經過定義門面的抽象類,「靜態代理」應用的服務容器中可用的底層類
門面的抽象類
應用服務容器輔助函數:根據參數判斷直接返回服務容器或者解析出相應抽象的實例
執行的順序以 「阿拉伯數字.」 作爲順序閱讀
https://github.com/leeprince/my_pattern/tree/master/my_minilaravel
這框架只是爲讓你們一塊兒瞭解和熟悉 laravel 在應用層的加載過程。固然 laravel 框架自己還有不少核心的架構,好比服務提供者、管道、中間件有時間我再補上
歡迎你們留言一塊兒討論~