1. 程式人生 > >PHP中的反射

PHP中的反射

  面向物件編輯中物件被賦予了自省的能力,而這個自省的過程就是反射.

   反射,直觀理解應時根據到達地找出出發地和來源.比方說,我給你一個光禿禿的物件,我可以僅僅通過這個物件就能知道它所屬的類,擁有哪些方法. 反射指在PHP執行狀態中,擴充套件分析PHP程式,匯出或提取出關於類,方法,屬性,引數等詳細資訊,包括註釋.這種動態獲取資訊以及動態呼叫物件方法的功能稱為反射API

如何使用反射API

以下面程式碼為例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class  HandsonBoy {      public  $name  'chenqionghe' ;     
public  $age  = 18;      public  function  __set( $name , $value )      {
         echo  '您正在設定私有屬性' . $name . '<br >值為' . $value . '<br>' ;          $this -> $name  $value ;      }      public  function  __get( $name )      {          if (!isset( $this -> $name ))          {              echo  '未設定' . $name ;              $this -> $name  "正在為你設定預設值" . '<br>' ;          }          return  $this -> $name ;      } } $boy  new  HandsonBoy(); echo  $boy ->name. '<br />' ; $boy ->hair =  'short' ;
現在,要獲取這個student物件的方法和屬性列表該怎麼做?可以用反射來實現,程式碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 $reflect  new  ReflectionObject( $boy ); $props  $reflect ->getProperties(); //獲取屬性的名字 foreach ( $props  as  $prop ) {      print  $prop ->getName().PHP_EOL; } //獲取物件方法列表 $methos  $reflect ->getMethods(); foreach ( $methos  as  $method ) {      print  $method ->getName().PHP_EOL; }

也可以不用反射API,使用class函式,返回物件屬性的關聯陣列以及更多的資訊:(針對於公開的屬性和):

1 2 3 4 5 6 //返回物件屬性的關聯陣列 var_dump(get_object_vars( $boy )); //類屬性 var_dump(get_class_vars(get_class( $boy ))); //返回由類的屬性的方法名組成的陣列 var_dump(get_class_methods(get_class( $boy )));

 反射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 31 32 33 /**   * @param $classObject 物件或者類名   */ function  getClass( $classObject ) {      $object  new  ReflectionClass( $classObject );      $className  $object ->getName();      $Methods  $Properties  array ();      foreach ( $object ->getProperties()  as  $v )      {          $Properties [ $v ->getName()] =  $v ;      }      foreach ( $object ->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" ; }

 不僅如此,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 27 28 29 30 31 32 33 34 <?php class  mysql {      function  connect( $db )      {          echo  "連線到資料庫{$db[0]}" .PHP_EOL;      } } class  sqlproxy {      private  $target ;      public  function  __construct( $tar )      {          $this ->target[] =  new  $tar ;      }      public  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" .PHP_EOL;                      $method ->invoke( $obj , $args );                      echo  "方法後攔截" .PHP_EOL;                  }              }          }      } } $obj  new  sqlproxy( 'mysql' ); $obj ->connect( 'chenqionghe' );
  這裡真正操作類是mysql類,但是sqlproxy實現了根據動態傳入引數,代替實際的類執行,並且在方法執行前後進行攔截,並且動態地改變類中的方法和屬性.這就是簡單的動態代理.      在平常的開發中用到反射的地方並不多: 一個是對物件進行除錯,別一個是獲取類的資訊.在MVC和外掛開發中,使用反射很常見,但是反射的消耗也很大,在可以找到替代方案的情況下,就不要濫用.      PHP有Token函式,可以通過這個機制實現一些反射功能.從簡單靈活的角度講,使用已經提供的反射API是可取的.     很多時候,善用反射能保持程式碼的優雅和簡潔,但反射也會破壞類的封裝性,因為反射可以使本不應該暴露的方法或屬性被強制暴露了出來,這既是優點也是缺點.