PHP具有完整的反射 API,提供了對類、接口、函數、方法和擴展進行逆向工程的能力。通過類的反射提供的能力我們能夠知道類是如何被定義的,它有什么屬性、什么方法、方法都有哪些參數,類文件的路徑是什么等很重要的信息。也正式因為類的反射很多PHP框架才能實現依賴注入自動解決類與類之間的依賴關系,這給我們平時的開發帶來了很大的方便。 本文主要是講解如何利用類的反射來實現依賴注入(Dependency Injection),并不會去逐條講述PHP Reflection里的每一個API,詳細的API參考信息請查閱官方文檔
為了更好地理解,我們通過一個例子來看類的反射,以及如何實現依賴注入。
下面這個類代表了坐標系里的一個點,有兩個屬性橫坐標x和縱坐標y。
14 | public function __construct( $x = 0, $y = 0) |
接下來這個類代表圓形,可以看到在它的構造函數里有一個參數是Point類的,即Circle類是依賴與Point類的。
15 | public function __construct(Point $point , $radius = 1) |
17 | $this ->center = $point ; |
18 | $this ->radius = $radius ; |
22 | public function printCenter() |
24 | printf( 'center coordinate is (%d, %d)' , $this ->center->x, $this ->center->y); |
28 | public function area() |
30 | return 3.14 * pow( $this ->radius, 2); |
ReflectionClass
下面我們通過反射來對Circle這個類進行反向工程。
把Circle類的名字傳遞給reflectionClass來實例化一個ReflectionClass類的對象。
1 | $reflectionClass = new reflectionClass(Circle:: class ); |
3 | object(ReflectionClass)#1 (1) { |
反射出類的常量
1 | $reflectionClass ->getConstants(); |
返回一個由常量名稱和值構成的關聯數組
通過反射獲取屬性
1 | $reflectionClass ->getProperties(); |
返回一個由ReflectionProperty對象構成的數組
03 | object(ReflectionProperty)#2 (2) { |
10 | object(ReflectionProperty)#3 (2) { |
反射出類中定義的方法
1 | $reflectionClass ->getMethods(); |
返回ReflectionMethod對象構成的數組
03 | object(ReflectionMethod)#2 (2) { |
05 | string(11) "__construct" |
10 | object(ReflectionMethod)#3 (2) { |
12 | string(11) "printCenter" |
17 | object(ReflectionMethod)#4 (2) { |
我們還可以通過getConstructor()來單獨獲取類的構造方法,其返回值為一個ReflectionMethod對象。
1 | $constructor = $reflectionClass ->getConstructor(); |
反射出方法的參數
1 | $parameters = $constructor ->getParameters(); |
其返回值為ReflectionParameter對象構成的數組。
03 | object(ReflectionParameter)#3 (1) { |
08 | object(ReflectionParameter)#4 (1) { |
依賴注入
好了接下來我們編寫一個名為make的函數,傳遞類名稱給make函數返回類的對象,在make里它會幫我們注入類的依賴,即在本例中幫我們注入Point對象給Circle類的構造方法。
02 | function make( $className ) |
04 | $reflectionClass = new ReflectionClass( $className ); |
05 | $constructor = $reflectionClass ->getConstructor(); |
06 | $parameters = $constructor ->getParameters(); |
07 | $dependencies = getDependencies( $parameters ); |
09 | return $reflectionClass ->newInstanceArgs( $dependencies ); |
13 | function getDependencies( $parameters ) |
16 | foreach ( $parameters as $parameter ) { |
17 | $dependency = $parameter ->getClass(); |
18 | if ( is_null ( $dependency )) { |
19 | if ( $parameter ->isDefaultValueAvailable()) { |
20 | $dependencies [] = $parameter ->getDefaultValue(); |
28 | $dependencies [] = '0' ; |
32 | $dependencies [] = make( $parameter ->getClass()->name); |
定義好make方法后我們通過它來幫我們實例化Circle類的對象:
01 | $circle = make( 'Circle' ); |
02 | $area = $circle ->area(); |
通過上面這個實例我簡單描述了一下如何利用PHP類的反射來實現依賴注入,Laravel的依賴注入也是通過這個思路來實現的,只不過設計的更精密大量地利用了閉包回調來應對各種復雜的依賴注入。
源碼分享:https://github.com/kevinyan815/php_reflection_dependency_injection_demo/blob/master/reflection.php