PHP反射機制

簡介

就算是類成員定義爲private也能夠在外部訪問,不用建立類的實例也能夠訪問類的成員和方法。php

PHP自5.0版本之後添加了反射機制,它提供了一套強大的反射API,容許你在PHP運行環境中,訪問和使用類、方法、屬性、參數和註釋等,其功能十分強大,常常用於高擴展的PHP框架,自動加載插件,自動生成文檔,甚至能夠用來擴展PHP語言。因爲它是PHP內建的oop擴展,爲語言自己自帶的特性,因此不須要額外添加擴展或者配置就可使用。更多內容見官方文檔數組

反射類型

PHP反射API會基於類,方法,屬性,參數等維護相應的反射類,已提供相應的調用API。框架

類型 說明
Reflector Reflector 是一個接口,被全部可導出的反射類所實現(implement)
Reflection 反射(reflection)類
ReflectionClass 報告了一個類的有關信息
ReflectionZendExtension 報告Zend擴展的相關信息
ReflectionExtension 報告了PHP擴展的有關信息
ReflectionFunction 報告了一個函數的有關信息
ReflectionFunctionAbstract ReflectionFunction 的父類
ReflectionMethod 報告了一個方法的有關信息
ReflectionObject 報告了一個對象(object)的相關信息
ReflectionParameter 取回了函數或方法參數的相關信息
ReflectionProperty 報告了類的屬性的相關信息

訪問

假設定義了一個類 User,咱們首先須要創建這個類的反射類實例,而後基於這個實例能夠訪問 User 中的屬性或者方法。無論類中定義的成員權限聲明是否爲public,均可以獲取到。函數

<?php 
namespace Extend;

use ReflectionClass;
use Exception;

/**
 * 用戶相關類
 * Class User
 * @package Extend
 */
class User{
    const ROLE = 'Students';
    public $username = '';
    private $password = '';

    public function __construct($username, $password)
    {
        $this->username = $username;
        $this->password = $password;
    }

    /**
     * 獲取用戶名
     * @return string
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * 設置用戶名
     * @param string $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * 獲取密碼
     * @return string
     */
    private function getPassword()
    {
        return $this->password;
    }

    /**
     * 設置密碼
     * @param string $password
     */
    private function setPassowrd($password)
    {
        $this->password = $password;
    }
}

$class = new ReflectionClass('Extend\User');  // 將類名User做爲參數,便可創建User類的反射類
$properties = $class->getProperties();  // 獲取User類的全部屬性,返回ReflectionProperty的數組
$property = $class->getProperty('password'); // 獲取User類的password屬性ReflectionProperty
$methods = $class->getMethods();   // 獲取User類的全部方法,返回ReflectionMethod數組
$method = $class->getMethod('getUsername');  // 獲取User類的getUsername方法的ReflectionMethod
$constants = $class->getConstants();   // 獲取全部常量,返回常量定義數組
$constant = $class->getConstant('ROLE');   // 獲取ROLE常量
$namespace = $class->getNamespaceName();  // 獲取類的命名空間
$comment_class = $class->getDocComment();  // 獲取User類的註釋文檔,即定義在類以前的註釋
$comment_method = $class->getMethod('getUsername')->getDocComment();  // 獲取User類中getUsername方法的註釋文檔

注意:建立反射類時傳送的類名,必須包含完整的命名空間,即便使用了 use 關鍵字。不然找不到類名會拋出異常。oop

交互

一旦建立了反射類的實例,咱們不只能夠經過反射類訪問原來類的方法和屬性,還能建立原來類的實例或則直接調用類裏面的方法。this

$class = new ReflectionClass('Extend\User');  // 將類名User做爲參數,便可創建User類的反射類
$instance = $class->newInstance('youyou', 1, '***');  // 建立User類的實例

$instance->setUsername('youyou_2');  // 調用User類的實例調用setUsername方法設置用戶名
$value = $instance->getUsername();   // 用過User類的實例調用getUsername方法獲取用戶名
echo $value;echo "\n";   // 輸出 youyou_2

$class->getProperty('username')->setValue($instance, 'youyou_3');  // 經過反射類ReflectionProperty設置指定實例的username屬性值
$value = $class->getProperty('username')->getValue($instance);  // 經過反射類ReflectionProperty獲取username的屬性值
echo $value;echo "\n";   // 輸出 youyou_3

$class->getMethod('setUsername')->invoke($instance, 'youyou_4'); // 經過反射類ReflectionMethod調用指定實例的方法,而且傳送參數
$value = $class->getMethod('getUsername')->invoke($instance);    // 經過反射類ReflectionMethod調用指定實例的方法
echo $value;echo "\n";   // 輸出 youyou_4

try {
    $property = $class->getProperty('password_1');
    $property->setAccessible(true);   // 修改 $property 對象的可訪問性
    $property->setValue($instance, 'password_2');  // 能夠執行
    $value = $property->getValue($instance);     // 能夠執行
    echo $value;echo "\n";   // 輸出 password_2
    $class->getProperty('password')->setAccessible(true);    // 修改臨時ReflectionProperty對象的可訪問性
    $class->getProperty('password')->setValue($instance, 'password');// 不能執行,拋出不能訪問異常
    $value = $class->getProperty('password')->getValue($instance);   // 不能執行,拋出不能訪問異常
    $value = $instance->password;   // 不能執行,類自己的屬性沒有被修改,仍然是private
}catch(Exception $e){echo $e;}

注意事項

  1. 直接訪問 protected 或則 private 的熟悉或者方法會拋出異常
  2. 須要調用指定的 ReflectionProperty 或則 ReflectionMethod 對象 setAccessible(true)方法才能訪問非公有成員
  3. 修改非公有成員的訪問權限只做用於當前的反射類的實例
  4. 須要注意獲取靜態成員和非靜態成員所使用的方法不同
  5. 獲取父類成員的方法和通常的不同

有時間會整理出反射類的API表,詳細的API列表能夠先查閱官方文檔spa

相關文章
相關標籤/搜索