近期忙著寫項目,沒有學習什麼特別新的東西,所以好長時間沒有更新博客。我們的項目用的是lumen,是基於laravel的一個輕量級框架,我看到裡面用到了一些反射API機制來幫助動態加載需要的類、判斷方法等,所以本篇文章就把在PHP中經常用到的反射API給大家分享一下吧,想學習反射API的同學可以看看。
說起反射ApI,自我感覺PHP中的反射ApI和java中的java.lang.reflect包差不多,都是由可以打印和分析類成員屬性、方法的一組內置類組成的。可能你已經學習過對象函數比如:get_class_vars()但是使用反射API會更加的靈活、輸出信息會更加詳細。
首先我們需要知道,反射API不僅僅是用來檢查類的,它本身包括一組類,用來完成各種功能:常用的類如下:
Reflection類 可以打印類的基本信息,(通過提供的靜態export()函數) ReflectionMethod類 見名知意,打印類方法、得到方法的具體信息等 ReflectionClass類 用於得到類信息,比如得到類包含的方法,類本的屬性,是不是抽象類等 ReflectionParameter類 顯示參數的信息,可以動態得到已知方法的傳參情況 ReflectionException類 用於顯示錯誤信息 ReflectionExtension類 得到PHP的擴展信息,可以判斷擴展是否存在等
傳統的打印類信息與反射APi的區別
下面是一段我自己寫的參數程序,用於演示反射的使用:
1 <?php
2
3 class Person
4 {
5 //成員屬性
6 public $name;
7 public $age;
8
9 //構造方法
10 public function __construct($name, $age)
11 {
12 $this->name = $name;
13 $this->age = $age;
14 }
15
16 //成員方法
17 public function set_name($name)
18 {
19 $this->$name = $name;
20 }
21
22 public function get_name()
23 {
24 return $this->$name;
25 }
26
27 public function get_age()
28 {
29 return $this->$age;
30 }
31
32 public function get_user_info()
33 {
34 $info = '姓名:' . $this->name;
35 $info .= ' 年齡:' . $this->age;
36 return $info;
37 }
38 }
39
40 class Teacher extends Person
41 {
42 private $salary = 0;
43
44 public function __construct($name, $age, $salary)
45 {
46 parent::__construct($name, $age);
47 $this->salary = $salary;
48 }
49
50 public function get_salary()
51 {
52 return $this->$salary;
53 }
54
55 public function get_user_info()
56 {
57 $info = parent::get_user_info();
58 $info .= " 工資:" . $this->salary;
59 return $info;
60 }
61 }
62
63 class Student extends Person
64 {
65 private $score = 0;
66
67 public function __construct($name, $age, $score)
68 {
69 parent::__construct($name, $age);
70 $this->score = $score;
71 }
72
73 public function get_score()
74 {
75 return $this->score;
76 }
77
78 public function get_user_info()
79 {
80 $info = parent::get_user_info();
81 $info .= " 成績:" . $this->score;
82 return $info;
83 }
84 }
85
86 header("Content-type:text/html;charset=utf8;");
87 $te_obj = new Teacher('李老師', '36', '2000');
88 $te_info = $te_obj->get_user_info();
89
90 $st_obj = new Student('小明', '13', '80');
91 $st_info = $st_obj->get_user_info();
我們先用var_dump();打印類的信息,如下所示,可以看出只是打印出類的簡單信息,甚至連方法也沒有,所以從這樣的信息中看不出其他游泳的信息。
var_dump($te_obj);
1 object(Teacher)#1 (3) {
2 ["salary":"Teacher":private]=>
3 string(4) "2000"
4 ["name"]=>
5 string(9) "李老師"
6 ["age"]=>
7 string(2) "36"
8 }
Reflection::export($obj);
我們利用Reflection提供的內置方法export來打印信息,如下所示:
打印出的信息比較完整,包括成員屬性,成員方法,類的基本信息,文件路徑,方法信息,方法屬性,傳參情況,所在文件的行數等等。比較全面的展示了類的信息。可以看出var_dump()或者print_r只能顯示類的簡要信息,好多信息根本顯示不出來,所以他們只能做簡單的調試之用,反射Api則提供的類更多的信息,可以很好地幫助我們知道調用類的情況,這對寫接口,特別是調用別人的接口提供了極大的便利。如果出了問題,也可以幫助調試。
1 object(Teacher)#1 (3) {
2 ["salary":"Teacher":private]=>
3 string(4) "2000"
4 ["name"]=>
5 string(9) "李老師"
6 ["age"]=>
7 string(2) "36"
8 }
9 Class [ class Person ] {
10 @@ /usr/local/www/phptest/oop/reflaction.php 3-38
11 - Constants [0] {
12 }
13 - Static properties [0] {
14 }
15 - Static methods [0] {
16 }
17 - Properties [2] {
18 Property [ public $name ]
19 Property [ public $age ]
20 }
21
22 - Methods [5] {
23 Method [ public method __construct ] {
24 @@ /usr/local/www/phptest/oop/reflaction.php 10 - 14
25
26 - Parameters [2] {
27 Parameter #0 [ $name ]
.....
反射API的具體使用:
看過框架源碼的同學都知道框架都可以加載第三方的插件、類庫等等。下面這個例子咱們借助反射APi簡單實現這個功能,該例子原型是我從書上學習的,我理解後按照自己的思路寫了一套:要實現的功能:用一個類去動態的遍歷調用Property類對象,類可以自由的加載其他的類的方法,而不用吧類嵌入到已有的代碼,也不用手動去調用類庫的代碼。
約定:每一個類要包含work方法,可以抽象出一個接口。可以把每個類的信息放在文件中,相當於各個類庫信息,通過類保存的Property類庫的對應對象,然後調用每個類庫的work方法。
下面是基礎代碼:
1 /*屬性接口*/
2 interface Property
3 {
4 function work();
5 }
6
7 class Person
8 {
9 public $name;
10 public function __construct($name)
11 {
12 $this->name = $name;
13 }
14 }
15
16 class StudentController implements Property
17 {
18 //set方法,但需要Person對象參數
19 public function setPerson(Person $obj_person)
20 {
21 echo 'Student ' . $obj_person->name;
22 }
23
24 //work方法簡單實現
25 public function work()
26 {
27 echo 'student working!';
28 }
29 }
30
31 class EngineController implements Property
32 {
33 //set方法
34 public function setWeight($weight)
35 {
36 echo 'this is engine -> set weight';
37 }
38
39 public function setPrice($price)
40 {
41 echo "this is engine -> set price";
42 }
43
44 //work方法簡單實現
45 public function work()
46 {
47 echo 'engine working!';
48 }
49 }
這裡定義了兩個相似類實現Property接口,同時都簡單實現work()方法 StudentController類稍微不同,參數需要Person對象,同時我們可以使用文件來保存各個類的信息,我們也可以用成員屬性代替。
1 class Run
2 {
3 public static $mod_arr = [];
4 public static $config = [
5 'StudentController' => [
6 'person' => 'xiao ming'
7 ],
8 'EngineController' => [
9 'weight' => '500kg',
10 'price' => '4000'
11 ]
12 ];
13
14 //加載初始化
15 public function __construct()
16 {
17 $config = self::$config;
18 //用於檢查是不是實現類
19 $property = new ReflectionClass('Property');
20
21 //
22 foreach ($config as $class_name => $params)
23 {
24 $class_reflect = new ReflectionClass($class_name);
25 if(!$class_reflect->isSubclassOf($property)) //用isSubclassOf方法檢查是否是這個對象
26 {
27 echo 'this is error';
28 continue;
29 }
30
31 //得到類的信息
32 $class_obj = $class_reflect->newInstance();
33 $class_method = $class_reflect->getMethods();
34
35 foreach ($class_method as $method_name)
36 {
37
38 $this->handle_method($class_obj, $method_name, $params);
39 }
40 array_push(self::$mod_arr, $class_obj);
41 }
42 }
43
44 //處理方法調用
45 public function handle_method(Property $class_obj, ReflectionMethod $method_name, $params)
46 {
47 $m_name = $method_name->getName();
48
49 $args = $method_name->getParameters();
50 if(count($args) != 1 || substr($m_name, 0, 3) != 'set')
51 {
52 return false;
53 }
54 //大小寫轉換,做容錯處理
55 $property = strtolower(substr($m_name, 3));
56
57 if(!isset($params[$property]))
58 {
59 return false;
60 }
61
62 $args_class = $args[0]->getClass();
63 echo '<pre>';
64 if(empty($args_class))
65 {
66 $method_name->invoke($class_obj, $params[$property]); //如果得到的類為空證明需要傳遞基礎類型參數
67 } else {
68 $method_name->invoke($class_obj, $args_class->newInstance($params[$property])); //如果不為空說明需要傳遞真實對象
69 }
70 }
71 }
72
73 //程序開始
74 new Run();
到此程序結束,Run啟動會自動調用構造方法,初始化要加載類庫的其他成員屬性,包括初始化和執行相應方法操作,這裡只是完成了對應的set方法。其中$mod_arr屬性保存了所有調用類的對象,每個對象包含數據,可以遍歷包含的對象來以此調用work()方法。
程序只做輔助理解反射PAI用,各個功能沒有完善,裡面用到了好多反射API的類,方法,下面會有各個方法的總結。
反射API提供的常用類和函數:
下面提供的函數是常用的函數,不是全部,有的函數根本用不到,所以我們有往撒謊那個寫,想看全部的可以到網上搜一下,比較多。提供的這組方法沒有必要背下來,用到的時候可以查看。
1 1:Reflection 2 public static export(Reflector r [,bool return])//打印類或方法的詳細信息 3 public static getModifierNames(int modifiers) //取得修飾符的名字 4 5 2:ReflectionMethod: 6 public static string export() //打印該方法的信息 7 public mixed invoke(stdclass object, mixed* args) //調用對應的方法 8 public mixed invokeArgs(stdclass object, array args)//調用對應的方法,傳多參數 9 public bool isFinal() //方法是否為final 10 public bool isAbstract() //方法是否為abstract 11 public bool isPublic() //方法是否為public 12 public bool isPrivate() //方法是否為private 13 public bool isProtected() //方法是否為protected 14 public bool isStatic() //方法是否為static 15 public bool isConstructor() //方法是否為構造函數 16 17 18 3:ReflectionClass: 19 public static string export() //打印類的詳細信息 20 public string getName() //取得類名或接口名 21 public bool isInternal() //類是否為系統內部類 22 public bool isUserDefined() //類是否為用戶自定義類 23 public bool isInstantiable() //類是否被實例化過 24 public bool hasMethod(string name) //類是否有特定的方法 25 public bool hasProperty(string name)//類是否有特定的屬性 26 public string getFileName() //獲取定義該類的文件名,包括路徑名 27 public int getStartLine() //獲取定義該類的開始行 28 public int getEndLine() //獲取定義該類的結束行 29 public string getDocComment() //獲取該類的注釋 30 public ReflectionMethod getConstructor() //取得該類的構造函數信息 31 public ReflectionMethod getMethod(string name) //取得該類的某個特定的方法信息 32 public ReflectionMethod[] getMethods() //取得該類的所有的方法信息 33 public ReflectionProperty getProperty(string name) //取得某個特定的屬性信息 34 public ReflectionProperty[] getProperties() //取得該類的所有屬性信息 35 public array getConstants() //取得該類所有常量信息 36 public mixed getConstant(string name) //取得該類特定常量信息 37 public ReflectionClass[] getInterfaces() //取得接口類信息 38 public bool isInterface() //測試該類是否為接口 39 public bool isAbstract() //測試該類是否為抽象類 40 41 4:ReflectionParameter: 42 public static string export() //導出該參數的詳細信息 43 public string getName() //取得參數名 44 public bool isPassedByReference() //測試該參數是否通過引用傳遞參數 45 public ReflectionClass getClass() //若該參數為對象,返回該對象的類名 46 public bool isArray() //測試該參數是否為數組類型 47 public bool allowsNull() //測試該參數是否允許為空 48 public bool isOptional() //測試該參數是否為可選的,當有默認參數時可選 49 public bool isDefaultValueAvailable() //測試該參數是否為默認參數 50 public mixed getDefaultValue() //取得該參數的默認值 51 52 5:ReflectionExtension類 53 54 public static export() //導出該擴展的所有信息 55 public string getName() //取得該擴展的名字 56 public string getVersion() //取得該擴展的版本 57 public ReflectionFunction[] getFunctions() //取得該擴展的所有函數 58 public array getConstants() //取得該擴展的所有常量 59 public array getINIEntries() //取得與該擴展相關的,在php.ini中的指令信息 60 }
寫的比較急,難免會有錯誤,還請大神們多多指正。
轉載請注明出處,謝啦!