本文來自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,每每也就能知道這個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: