php的反射技术,PHP 反射使用
上一節 主要系統講了反射的用法,雖然講解了用法,但是沒有對其在項目中的實際使用做講解,不學以致用,不如不學。
在好多框架底層實現上面使用了反射,所以要理解和分析框架底層源碼的話,必須掌握反射,不然的話理解十分的困難。
下面我們講下反射在實際開發中的應用。
自動生成文檔
實現 MVC 架構
實現單元測試
配合 DI 容器解決依賴
…
自動生成文檔
根據反射的分析類,接口,函數和方法的內部結構,方法和函數的參數,以及類的屬性和方法,可以自動生成文檔。
/**
* 學生類
*
* 描述信息
*/
class Student
{
const NORMAL = 1;
const FORBIDDEN = 2;
/**
* 用戶ID
* @var 類型
*/
public $id;
/**
* 獲取id
* @return int
*/
public function getId()
{
return $this->id;
}
public function setId($id = 1)
{
$this->id = $id;
}
}
$ref = new ReflectionClass('Student');
$doc = $ref->getDocComment();
echo $ref->getName() . ':' . getComment($ref) , "\n";
echo "屬性列表:\n";
printf("%-15s%-10s%-40s\n", 'Name', 'Access', 'Comment');
$attr = $ref->getProperties();
foreach ($attr as $row) {
printf("%-15s%-10s%-40s\n", $row->getName(), getAccess($row), getComment($row));
}
echo "常量列表:\n";
printf("%-15s%-10s\n", 'Name', 'Value');
$const = $ref->getConstants();
foreach ($const as $key => $val) {
printf("%-15s%-10s\n", $key, $val);
}
echo "\n\n";
echo "方法列表\n";
printf("%-15s%-10s%-30s%-40s\n", 'Name', 'Access', 'Params', 'Comment');
$methods = $ref->getMethods();
foreach ($methods as $row) {
printf("%-15s%-10s%-30s%-40s\n", $row->getName(), getAccess($row), getParams($row), getComment($row));
}
// 獲取權限
function getAccess($method)
{
if ($method->isPublic()) {
return 'Public';
}
if ($method->isProtected()) {
return 'Protected';
}
if ($method->isPrivate()) {
return 'Private';
}
}
// 獲取方法參數信息
function getParams($method)
{
$str = '';
$parameters = $method->getParameters();
foreach ($parameters as $row) {
$str .= $row->getName() . ',';
if ($row->isDefaultValueAvailable()) {
$str .= "Default: {$row->getDefaultValue()}";
}
}
return $str ? $str : '';
}
// 獲取注釋
function getComment($var)
{
$comment = $var->getDocComment();
// 簡單的獲取了第一行的信息,這里可以自行擴展
preg_match('/\* (.*) *?/', $comment, $res);
return isset($res[1]) ? $res[1] : '';
}
復制代碼
運行 php file.php 就可以看到相應的文檔信息。
實現 MVC 架構
現在好多框架都是 MVC 的架構,根據路由信息定位 控制器(controller)和方法(controller) 和方法(controller)和方法(method) 的名稱,之后使用反射實現自動調用。
$class = new ReflectionClass(ucfirst($controller) . 'Controller');
$controller = $class->newInstance();
if ($class->hasMethod($method)) {
$method = $class->getMethod($method);
$method->invokeArgs($controller, $arguments);
} else {
throw new Exception("{$controller} controller method {$method} not exists!");
}
復制代碼
實現單元測試
一般情況下我們會對函數和類進行測試,判斷其是否能夠按我們預期返回結果,我們可以用反射實現一個簡單通用的類測試用例。
class Calc
{
public function plus($a, $b)
{
return $a + $b;
}
public function minus($a, $b)
{
return $a - $b;
}
}
function testEqual($method, $assert, $data)
{
$arr = explode('@', $method);
$class = $arr[0];
$method = $arr[1];
$ref = new ReflectionClass($class);
if ($ref->hasMethod($method)) {
$method = $ref->getMethod($method);
$res = $method->invokeArgs(new $class, $data);
var_dump($res === $assert);
}
}
testEqual('Calc@plus', 3, [1, 2]);
testEqual('Calc@minus', -1, [1, 2]);
復制代碼
這是類的測試方法,也可以利用反射實現函數的測試方法。
這里只是我簡單寫的一個測試用例,PHPUnit 單元測試框架很大程度上依賴了 Reflection 的特性,可以了解下。
配合 DI 容器解決依賴
Laravel 等許多框架都是使用 Reflection 解決依賴注入問題,具體可查看 Laravel 源碼進行分析。
下面我們代碼簡單實現一個 DI 容器演示 Reflection 解決依賴注入問題。
class DI
{
protected static $data = [];
public function __set($k, $v)
{
self::$data[$k] = $v;
}
public function __get($k)
{
return $this->bulid(self::$data[$k]);
}
// 獲取實例
public function bulid($className)
{
// 如果是匿名函數,直接執行,并返回結果
if ($className instanceof Closure) {
return $className($this);
}
// 已經是實例化對象的話,直接返回
if(is_object($className)) {
return $className;
}
// 如果是類的話,使用反射加載
$ref = new ReflectionClass($className);
// 監測類是否可實例化
if (!$ref->isInstantiable()) {
throw new Exception('class' . $className . ' not find');
}
// 獲取構造函數
$construtor = $ref->getConstructor();
// 無構造函數,直接實例化返回
if (is_null($construtor)) {
return new $className;
}
// 獲取構造函數參數
$params = $construtor->getParameters();
// 解析構造函數
$dependencies = $this->getDependecies($params);
// 創建新實例
return $ref->newInstanceArgs($dependencies);
}
// 分析參數,如果參數中出現依賴類,遞歸實例化
public function getDependecies($params)
{
$data = [];
foreach($params as $param)
{
$tmp = $param->getClass();
if (is_null($tmp)) {
$data[] = $this->setDefault($param);
} else {
$data[] = $this->bulid($tmp->name);
}
}
return $data;
}
// 設置默認值
public function setDefault($param)
{
if ($param->isDefaultValueAvailable()) {
return $param->getDefaultValue();
}
throw new Exception('no default value!');
}
}
class Demo
{
public function __construct(Calc $calc)
{
echo $calc->plus(1, 2);
}
}
$di = new DI();
$di->calc = 'Calc'; // 加載單元測試用例中 Calc 類
$di->demo = 'Demo';
$di->demo;
復制代碼
注意上面的 calc 和 demo 的順序,不能顛倒,不然的話會報錯,原因是由于 Demo 依賴 Calc,首先要定義依賴關系。
在 Demo 實例化的時候,會用到 Calc 類,也就是說 Demo 依賴于 Calc,但是在 $data 上面找不到的話,會拋出錯誤,所以首先要定義 $di->calc = 'Calc'。
Reflection 是一個非常 Cool 的功能,使用它,但不要濫用它。
?版權聲明:原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 & 作者信息。
End
堅持原創技術分享,您的支持將鼓勵我繼續創作! 賞
微信打賞
支付寶打賞
總結
以上是生活随笔為你收集整理的php的反射技术,PHP 反射使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php 半角全角,PHP 全角转半角实现
- 下一篇: php替换文件中的数据库,批量替换php