PHP反射機制原理與用法

反射

反射,直觀理解就是根據到達地找到出發地和來源。好比,一個光禿禿的對象,咱們能夠僅僅經過這個對象就能知道它所屬的類、擁有哪些方法php

反射是指在PHP運行狀態中,擴展分析PHP程序,導出或提出關於類、方法、屬性、參數等的詳細信息,包括註釋。這種動態獲取信息以及動態調用對象方法的功能稱爲反射API。mysql

 

php類的成員變量沒有在類中聲明,而是在函數中聲明,有什麼不一樣?

    ReflectionClass: 獲取類聲明時的結構sql

    ReflectionObject: 可獲取類實例化後的結構數據庫

 

代碼以下:
class test{
    private $name;
    private $sex;
    function __construct(){
        $this->aaa='aaa';
    }
}
$test=new test();
$reflect=new ReflectionClass($test);
$pro=$reflect->getDefaultProperties();
print_r($pro);//打印結果:Array ( [name] => [sex] => )
echo $test->aaa;//打印結果:aaa數組


在這個test類中,聲明瞭兩個成員變量$name和$sex,可是在構造函數中,又聲明瞭一個變量$aaa,初始化類,使用反射類打印默認成員屬性只有聲明的兩個成員變量屬性,可是打印類的$aaa變量發現仍是能夠輸出結果。
請問類的成員變量不用聲明,在函數中聲明也是能夠的嗎,有什麼區別?
在你這個例子中,使用ReflectionClass是不恰當的,由於__construct只有在實例化class時,纔會執行。
也就是說ReflectionClass更多的是反射類聲明時的結構,而不是類實例化後的結構,因此沒有輸出屬性aaa是正確,由於屬性aaa確實是(在類聲明時)不存在的。
那麼怎麼看屬性aaa呢,應該用ReflectionObject反射實例化後的結構,例如函數

代碼以下:
<?php
class test{
    private $name;
    private $sex;
    function __construct(){
        $this->aaa='aaa';
    }
}
$test=new test();
$reflect=new ReflectionObject($test);
$pro=$reflect->getProperties();
print_r($pro);this


通過實例化之後,屬性aaa纔會存在,這時你就能看到屬性aaa了spa

 

如何使用反射API

例子:插件

1代理

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<?php

class person{

public $name;

public $gender;

public function say(){

  echo $this->name," \tis ",$this->gender,"\r\n";

}

public function set($name, $value) {

  echo "Setting $name to $value \r\n";

  $this->$name= $value;

}

public function get($name) {

  if(!isset($this->$name)){

    echo '未設置';

       

     $this->$name="正在爲你設置默認值";

  }

  return $this->$name;

  }

}

$student=new person();

$student->name='Tom';

$student->gender='male';

$student->age=24;

如今,要獲取這個student對象的方法和屬性列表該怎麼作呢?如如下代碼所示:

1

2

3

4

5

6

7

8

9

10

11

// 獲取對象屬性列表

$reflect = new ReflectionObject($student);

$props = $reflect->getProperties();

foreach ($props as $prop) {

  print $prop->getName() ."\n";

}

// 獲取對象方法列表

$m=$reflect->getMethods();

foreach ($m as $prop) {

  print $prop->getName() ."\n";

}

也能夠不用反射API,使用class函數,返回對象屬性的關聯數組以及更多的信息:

1

2

3

4

5

6

// 返回對象屬性的關聯數組

var_dump(get_object_vars($student));

// 類屬性

var_dump(get_class_vars(get_class($student)));

// 返回由類的方法名組成的數組

var_dump(get_class_methods(get_class($student)));

假如這個對象是從其餘頁面傳過來的,怎麼知道它屬於哪一個類呢?一句代碼就能夠搞定:

1

2

// 獲取對象屬性列表所屬的類

echo get_class($student);

反射API的功能顯然更強大,甚至能還原這個類的原型,包括方法的訪問權限等,如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

// 反射獲取類的原型

$obj = new ReflectionClass('person');

$className = $obj->getName();

$Methods = $Properties = array();

foreach($obj->getProperties() as $v){

    $Properties$v->getName()] = $v;

}

foreach($obj->getMethods() as $v){

    $Methods$v->getName()] = $v;

}

echo "class {$className}\n{\n";

is_array($Properties)&&ksort($Properties);

foreach($Properties as $k => $v){

    echo "\t";

    echo $v->isPublic() ? ' public' : '',$v->isPrivate() ? ' private' : '',

    $v->isProtected() ? ' protected' : '',

    $v->isStatic() ? ' static' : '';

    echo "\t{$k}\n";

}

echo "\n";

if(is_array($Methods)) ksort($Methods);

foreach($Methods as $k => $v){

    echo "\tfunction {$k}(){}\n";

}

echo "}\n";

輸出以下:

1

2

3

4

5

6

7

8

class person

{

  public gender

  public name

  function get(){}

  function set(){}

  function say(){}

}

不只如此,PHP手冊中關於反射API更是有幾十個,能夠說,反射完整地描述了一個類或者對象的原型。反射不只能夠用於類和對象,還能夠用於函數、擴展模塊、異常等。

 

反射有什麼做用

反射能夠用於文檔生成。所以能夠用它對文件裏的類進行掃描,逐個生成描述文檔。

既然反射能夠探知類的內部結構,那麼是否是能夠用它作hook實現插件功能呢?或者是作動態代理呢?

例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

<?php

class mysql {

  function connect($db) {

    echo "鏈接到數據庫${db[0]}\r\n";

  }

}

class sqlproxy {

  private $target

  function construct($tar) {

    $this->target[] = new $tar();

  }

  function call($name, $args) {

    foreach ($this->target as $obj) {

      $r = new ReflectionClass($obj);

      if ($method = $r->getMethod($name)) {

        if ($method->isPublic() && !$method->isAbstract()) {

          echo "方法前攔截記錄LOG\r\n";

          $method->invoke($obj, $args);

          echo "方法後攔截\r\n";

        }

      }

    }

  }

}

$obj = new sqlproxy('mysql');

$obj->connect('member');

在日常開發中,用到反射的地方很少:一個是對對象進行調試,另外一個是獲取類的信息。在MVC和插件開發中,使用反射很常見,可是反射的消耗也很大,在能夠找到替代方案的狀況下,就不要濫用。

PHP有Token函數,能夠經過這個機制實現一些反射功能。從簡單靈活的角度講,使用已經提供的反射API是可取的。

不少時候,善用反射能保持代碼的優雅和簡潔,但反射也會破壞類的封裝性,由於反射可使本不該該暴露的方法或屬性被強制暴露了出來,這既是優勢也是缺點。

相關文章
相關標籤/搜索