Laravel底層學習筆記01 - 框架核心,依賴注入,閉包

IT小馬發表於2021-11-04

參考資料:
laravel底層核心程式碼分析之核心概念講解
PHP控制反轉(IOC)和依賴注入(DI)
閉包匿名函式,還在傻傻搞不清楚嗎?

Laravel框架核心

優點

  1. 整合了composer
  2. 實現了依賴注入,更好的管理類的依賴關係,方便擴充套件(相對於MVC模式)
  3. 高階特性:控制檯console,事件event,佇列queue,中介軟體middleware,門面模式facades
  4. 核心概念:服務容器 serviceProvider

缺點及優化

缺點

  1. 載入檔案太多,訪問速度慢

優化

  1. 快取配置檔案
  2. 去掉不必要的載入檔案(主要是serviceProvider)
  3. 開啟Opcache

框架啟動流程(生命週期)

  1. 引用自動載入檔案
  2. 生成服務容器
    1) 註冊基礎的bingings
    2) 通過bind註冊服務容器,事件,路由,日誌服務
    3) 通過bind繫結介面
  3. 獲取Request物件
  4. 邏輯處理
    1) 解析啟動項(基礎服務)如路由,異常處理,門面,服務容器
    2) 通過管道模式,用中介軟體過濾使用者請求,處理業務邏輯
  5. 返回Response物件

知識點

單例模式,觀察者模式,管道模式
依賴注入,匿名函式,反射
預定義介面ArrayAccess

控制反轉和依賴注入

控制反轉 IOC(inversion of control)

將元件間的依賴關係從程式內部提到外部來管理

解釋:不在A類中直接新建B類例項,而是通過IOC容器將B類的例項傳給A

依賴注入 DI(dependency injection)

將元件的依賴通過外部以引數或其他形式注入

示例:

class DbMysql
{
    public function query(){}
}
class IOC
{
    public $db;
    public function __construct($dbMysql)
    {
        $this->db = $dbMysql;
    }
    public function action()
    {
        $this->db->query();
    }
}
$db = new DbMysql();
$c = new IOC($db);
$c->action();

IOC類中不需要例項化DbMysql,而是將DbMysql的例項作為引數傳入,僅呼叫DbMysql的方法。這種模式就是依賴注入

將B類的例項化動作提出到IOC類的外面,就叫做控制反轉

PHP的反射機制

在PHP執行時,擴充套件分析程式,匯出或提出關於類,方法,屬性,引數的詳細資訊,這種動態獲取和呼叫資訊的功能稱為反射API。
class A 
{
    public function __construct(B $b)
    {
    }
}
class B 
{
}
//獲取類的反射資訊(所有資訊)
$reflector = new ReflectionClass('A');
//獲取建構函式
$constructor = $reflector->getConstructor();
//獲取建構函式引數
$dependencies = $constructor->getParameters();
//獲取依賴的類名
foreach ($dependencies as $dependency){
    if(!is_null($dependency->getClass())){
        $classname = $dependency->getClass()->name;
        $p[] = new $classname();
    }
}
//從給出的引數建立一個新的類例項
$a = $reflector->newInstanceArgs($p);

如果B類也有依賴的類,則需要通過遞迴建立

<?php 
class A
{
    public function __construct(B $b)
    {
        $this->b = $b;
    }
    public function getB()
    {
        $this->b->bMethod();
    }
}
class B
{
    public function __construct(C $c,D $d)
    {
        $this->c = $c;
        $this->d = $d;
    }
    public  function bMethod()
    {
        echo "我是B中的方法bMethod()";
    }
}

class C{
    public function __construct()
    {
    }
    public function cMethod(){
        echo "我是C中的方法cMethod()";
    }
}

class D{
    public function __construct()
    {
    }
    public function dMethod(){
        echo "我是D中的方法dMethod()";
    }
}
class Ioc
{
    protected $instances = [];
    public function __construct()
    {
    }
    public function getInstance($classname){
        $reflector = new ReflectionClass($classname);
        $constructor = $reflector->getConstructor();
        $dependencies = $constructor->getParameters();
        if(!$dependencies){
            return new $classname();
        }
        foreach ($dependencies as $dependency){
            if(!is_null($dependency->getClass())){
                $instances[] = $this->make($dependency->getClass()->name);
            }
        }
        return $reflector->newInstanceArgs($instances);
    }
    public function make($classname){
        return $this->getInstance($classname);
    }
}
$ioc = new Ioc();
$a = $ioc->make('A');
$a->getB();

總結

PHP程式執行的本質:包含檔案,獲取例項化物件。
傳統框架:通過include/require來管理類的依賴關係。
Laravel:通過namespace和use,實現了自動載入機制,找到類所在檔案,然後通過反射獲取類的例項化物件。

學習資料:
laravel底層核心程式碼分析之匿名函式
閉包匿名函式,還在傻傻搞不清楚嗎?

匿名函式

回撥函式

呼叫函式的時候將另一個函式作為引數傳遞到呼叫的函式中,而不是傳遞一個普通的變數作為引數

使用回撥函式是為了可以將一段自己定義的功能傳到函式內部使用

function test($v){
    return $v * $v;
}
$a = array(1, 2, 3);
print_r(array_map("test", $a)); //[1, 4, 9]

匿名函式

沒有名字的函式就是匿名函式

1.普通使用

$func = function($str){
    echo $str;
};
$func('Hello');

2.使用use傳遞外部變數

$b = "World";
$func = function($str) use($b){
    echo $str. " ". $b;
};
$func("Hello");

閉包(closures)

php的閉包(Closure)也就是匿名函式。是PHP5.3引入的。
閉包函式就是能夠讀取其他函式內部變數的函式
閉包 = 匿名函式 + use
function getMoney($a, $b){
    return function($p) use ($a, $b){
        echo $p. '-'. $a. '-'. $b;
    };
}
$closure = getMoney('1', '2');
var_dump($closure instanceof Closure);//true
$closure('test');//test-1-2

實際應用

1. 當做回撥函式

src/Illuminate/Routing/Router.php
    public function gatherRouteMiddleware(Route $route)
    {
        $middleware = collect($route->gatherMiddleware())->map(function ($name) {
            return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
        })->flatten();

        return $this->sortMiddleware($middleware);
    }

2. 當做閉包

src/Illuminate/Container/Container.php
    //建立
    protected function getClosure($abstract, $concrete)
    {
        return function ($container, $parameters = []) use ($abstract, $concrete) {
            if ($abstract == $concrete) {
                return $container->build($concrete);
            }

            return $container->make($concrete, $parameters);
        };
    }
    //繫結
    function bind($abstract, $concrete = null, $shared = false)
    {
       ...
        if (! $concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }
        ...
    }
    //呼叫
    public function build($concrete)
    {
        if ($concrete instanceof Closure) {
            return $concrete($this, $this->getLastParameterOverride());
        }
        ...
    }

注意事項

1. 匿名函式只有在呼叫的時候才會真正解析
2. 呼叫匿名函式時需要傳遞function後面的引數

相關文章