Laravel自動依賴解析的背後實現——PHP映射解析(reflection api\reflection class)功能

本文來自pilishen.com----原文連接; 歡迎來和pilishen一塊兒學習php&Laravel;學習羣:109256050php

該篇屬於《Laravel底層核心技術實戰揭祕》這一課程《laravel底層核心概念解析》這一章的擴展閱讀。因爲要真正學好laravel底層,有些PHP相關的知識必須得了解,考慮到學員們的基礎差別,爲了不視頻當中過於詳細而連篇累牘,故將一些laravel底層實現相關的PHP知識點以文章形式呈現,供你們預習和隨時查閱。laravel

以前的文章裏,咱們知道laravel的IOC Container能自動解析依賴,很逆天很神奇,那麼它背後的實現原理是怎麼樣的呢?裏面有什麼rocket science呢?api

其實也沒啥,背後用的是PHP5開始自帶的映射(reflection)功能,或者說反射功能,又常常稱做是reflection api,它能反向地解析提交給它的class、method、extension等,基於這些信息,你能夠分析出一個class的類型,須要哪些依賴,有哪些屬性,父類子類狀況等等,而後去相應地構建實例,就能夠實現laravel的自動依賴解析功能了。bash

這裏呢,咱們先不看laravel自動依賴解析的具體代碼,咱們先來看看這個PHP的reflection api是什麼鬼,尤爲是其中的ReflectionClass,也便是專門用來反向解析class的。ide

class Foo
{
    public $name = 'pilishen';
    public $project = 'laravel';
    protected $bar;

    //Constructor
    public function __construct(Bar $bar)
    {
        $this->bar = $bar;
    }
   
    public function name()
    {
        echo $this->name."\n";
    }

    public function project()
    {
        echo $this->project."\n";
    }
}
複製代碼

獲取類名、命名空間、文件名:

$reflection = new ReflectionClass('Foo');
echo $reflection->getName();
複製代碼

就能輸出Foo也即這個class的name,相關的還有一個很明顯的getShortName().post

若是你想獲取該class所在的文件路徑及名稱,那麼可使用getFileName()方法,好比個人顯示:學習

string '/home/vagrant/Code/php-test/index.php' (length=37)
複製代碼

固然,獲取命名空間(namespace)就是getNamespaceName()ui

獲取各種屬性或參數:

var_dump($reflection->getDefaultProperties());
複製代碼

就能獲取其默認屬性及值:this

array (size=3)
  'name' => string 'pilishen' (length=8)
  'project' => string 'laravel' (length=7)
  'bar' => null
複製代碼

可能你會想到get_class_vars或者get_object_vars,假設這個時候咱們只想獲取其protected屬性怎麼辦呢?spa

$props = $reflection->getProperties(ReflectionProperty::IS_PROTECTED);
var_dump($props);
複製代碼

這個時候顯示:

array (size=1)
  0 => 
    object(ReflectionProperty)[2]
      public 'name' => string 'bar' (length=3)
      public 'class' => string 'Foo' (length=3)
複製代碼

也便可以經過在getProperties()中傳遞filter參數來篩選要獲取的屬性,固然實際當中,你能夠經過下面的方式來分別獲取每一個屬性的name:

foreach ($props as $prop) {
    print $prop->getName() . "\n";
}
複製代碼

屬性相關的其餘方法:

getProperty() :獲取某一個特定屬性,好比 $class->getProperty('name');

getStaticProperties() :獲取全部的靜態屬性

getStaticPropertyValue() :獲取特定的靜態屬性的value

`setStaticPropertyValue()` :將某個已有的靜態屬性值設爲新的值,注意必須是已有的,你不能經過它來添加新的靜態屬性

`hasProperty()` :查看某個特定的屬性是否存在

`hasConstant()` :查看某個特定的常量(const)是否存在
複製代碼

獲取constructor信息:

說白了一旦獲取到了constructor,每每也就能知道這個class的依賴有哪些了,執行:

var_dump($reflection->getConstructor());
複製代碼

就能夠看到:

object(ReflectionMethod)[2]
  public 'name' => string '__construct' (length=11)
  public 'class' => string 'Foo' (length=3)
複製代碼

若是不存在constructor就會返回null,因此實際當中能夠經過is_null()來作進一步判斷。接下來執行:

$constructor = $reflection->getConstructor();
var_dump($constructor->getParameters());
複製代碼

就會以array的形式返回constructor裏的具體信息,每一條都是一個object:

array (size=1)
  0 => 
    object(ReflectionParameter)[3]
      public 'name' => string 'bar' (length=3)
複製代碼

而後咱們就能夠經過遍歷的形式獲取每個具體的parameter,在每一個parameter上去獲取它相應的類型聲明(type declaration)

$constructor = $reflection->getConstructor();
$parameters = $constructor->getParameters();
foreach ($parameters as $parameter) {
	var_dump($parameter->getClass());
}
複製代碼

就能夠看到:

object(ReflectionClass)[4]
  public 'name' => string 'Bar' (length=3)
複製代碼

若是不存在class,那麼返回的是null,說明傳的只是一個普通參數,沒有進行類聲明,就能夠進行其餘相應操做,好比能夠調用isDefaultValueAvailable()來判斷這個參數有沒有默認值,而後經過getDefaultValue()來獲取其默認值。而返回的$class = $parameter->getClass(),能夠進一步經過$class->name獲取其class名稱,而後就能夠相應地去構建依賴實例了。

跟自動構建實例相關的其餘方法:

isInstantiable() : 判斷一個Class或者傳參可否被實例化,好比interface和abstract class就不能被實例化,這個通常用在進行反向解析最開始的地方,好比若是不能實例化,也就不必去獲取其constructor相關信息了;

newInstanceArgs() : 基於你傳遞的參數來建立一個新的實例,這裏傳進去的參數,也就是constructor裏須要傳進去的參數,若是是相應的依賴,你須要傳遞相應依賴的實例,接收的是array的形式;

知道了以上的方法,你就能夠自行嘗試反向解析某一個class,而後分析出其從屬依賴,而後返回一個自動構建依賴的class實例,試試吧~ :stuck_out_tongue_winking_eye:

固然,這期間你能夠看看laravel Container class的相關源碼,相信理解起來就不會太難,在接下來《Laravel底層核心技術實戰揭祕》裏,咱們將在視頻裏帶領你們一塊兒寫一個本身的自動依賴解析功能,而後帶你們一塊兒看看laravel是怎麼實現的,一塊兒揭開laravel IOC Container的魔力所在~ :muscle:

相關文章
相關標籤/搜索