摘要:反射在每個面向對象的編程語言中都存在,它的主要目的就是在運行時分析類或者對象的狀態,導出或提取出關于類方法屬性參數等的詳細信息,包括注釋。反射是操縱面向對象范型中元模型的,可用于構建復雜,可擴展的應用。
反射在每個面向對象的編程語言中都存在,它的主要目的就是在運行時分析類或者對象的狀態,導出或提取出關于類、方法、屬性、參數等的詳細信息,包括注釋。 反射是操縱面向對象范型中元模型的 API,可用于構建復雜,可擴展的應用。反射在日常的 Web 開發中其實用的不多,更多的是在偏向底層一些的代碼中,比如說框架的底層中依賴注入、對象池、動態代理、自動獲取插件列表、自動生成文檔以及一些設計模式等等,都會大量運用到反射技術。
PHP 的反射 API 很多,但是常用的一般都是 ReflectionClass 和 ReflectionMethod:
1.ReflectionClass
這個是用來獲取類的信息,可以簡單測試一下:
class Student { private $name; public function setName($name) { $this->name = $name; } protected function getName() { return $this->name; } }
獲取類的方法列表:
$ref = new ReflectionClass(Student::class); var_dump($ref->getMethods());
返回的是一個 ReflectionMethod 的數組:
array(1) { [0]=> object(ReflectionMethod)#2 (2) { ["name"]=> string(7) "setName" ["class"]=> string(7) "Student" } }
附上一些常用方法,詳細的可以查看文檔:
ReflectionClass::getMethods 獲取方法的數組 ReflectionClass::getName 獲取類名 ReflectionClass::hasMethod 檢查方法是否已定義 ReflectionClass::hasProperty 檢查屬性是否已定義 ReflectionClass::isAbstract 檢查類是否是抽象類(abstract) ReflectionClass::isFinal 檢查類是否聲明為 final ReflectionClass::isInstantiable 檢查類是否可實例化 ReflectionClass::newInstance 從指定的參數創建一個新的類實例
2.ReflectionMethod
這個主要是針對方法的反射,我們可以簡單執行一下:
$stu = new Student(); $ref = new ReflectionClass(Student::class); $method = $ref->getMethod("setName"); $method->invoke($stu, "john"); var_dump($stu->name);
可以輸出:
john
附上一些常用的方法,詳細的可以去看看文檔:
ReflectionMethod::invoke 執行 ReflectionMethod::invokeArgs 帶參數執行 ReflectionMethod::isAbstract 判斷方法是否是抽象方法 ReflectionMethod::isConstructor 判斷方法是否是構造方法 ReflectionMethod::isDestructor 判斷方法是否是析構方法 ReflectionMethod::isFinal 判斷方法是否定義 final ReflectionMethod::isPrivate 判斷方法是否是私有方法 ReflectionMethod::isProtected 判斷方法是否是保護方法 (protected) ReflectionMethod::isPublic 判斷方法是否是公開方法 ReflectionMethod::isStatic 判斷方法是否是靜態方法 ReflectionMethod::setAccessible 設置方法是否訪問
接下來說一些反射在實際開發中比較常見的應用。
執行私有方法其實反射不僅可以執行私有方法,還可以讀取私有屬性。這個主要應用在一些設計不合理的 SDK 里面,一些很好用的方法和屬性卻不對外開放。
class Student { private $name; private function setName($name) { $this->name = $name; } }
執行私有方法:
$stu = new Student(); $ref = new ReflectionClass($stu); $method = $ref->getMethod("setName"); $method->setAccessible(true); $method->invoke($stu, "john");
讀取私有屬性:
$stu = new Student(); $ref = new ReflectionClass($stu); $prop = $ref->getProperty("name"); $prop->setAccessible(true); $val = $prop->getValue($stu); var_dump($val);動態代理
其實 PHP 有魔術方法,所以實現動態代理已經很簡單了,但是通過魔術方法來實現的都不完美,個人理解最好的實現應該還是 JDK 中的動態代理,基于一個接口進行掃描實現實在 PHP 中也可以實現。我們先來看看動態代理在 JDK 中是怎么使用的:
1.首先定義一個實現類的接口,JDK 的動態代理必須基于接口(Cglib則不用)
package com.yao.proxy; public interface Helloworld { void sayHello(); }
2.定義一個實現類,這個類就是要被代理的對象
package com.yao.proxy; import com.yao.HelloWorld; public class HelloworldImpl implements HelloWorld { public void sayHello() { System.out.print("hello world"); } }
3.調用被代理對象方法的實現類
package com.yao.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler{ private Object target; public MyInvocationHandler(Object target) { this.target=target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前置工作!"); Object obj = method.invoke(target,args); System.out.println("后置工作!"); return obj; }
4.測試
package com.yao.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Demo { public static void main(String[] args) { HelloworldImpl realSubject = new HelloworldImpl(); MyInvocationHandler handler = new MyInvocationHandler(realSubject); ClassLoader loader = realSubject.getClass().getClassLoader(); Class[] interfaces = realSubject.getClass().getInterfaces(); HelloworldImpl proxySubject = (HelloworldImpl) Proxy.newProxyInstance(loader, interfaces, handler); String hello = proxySubject.sayHello(); } }
JDK 的動態代理在底層實際上是掃描實現的接口,然后動態生成類的字節碼文件。PHP 是動態語言,所以可以用 eval 來實現。
1.定義調度器接口
interface InvocationHandler { function invoke($method, array $arr_args); }
2.動態代理實現
定義一個類的 stub:
return new Class($handler,$target) implements %s { private $handler; private $target; public function __construct(InvocationHandler $handler, $target) { $this->handler = $handler; $this->target = $target; } %s };
定義一個方法的 stub:
public function %s(%s) { $args = func_get_args(); $method = explode("::", __METHOD__); $this->handler->invoke(new ReflectionMethod($this->target, $method[1]), $args); }
Proxy 實現:
final class Proxy { const CLASS_TEMPLATE = class_stub; //這里顯示上面定義的,為了方便閱讀 const FUNCTION_TEMPLATE = function_stub; //同上 public static function newProxyInstance($target, array $interfaces, InvocationHandler $handler) { } protected static function generateClass(array $interfaces) { } protected static function checkInterfaceExists(array $interfaces) { } }
其中 newProxyInstance 和 generateClass 代碼:
public static function newProxyInstance($target, array $interfaces, InvocationHandler $handler) { self::checkInterfaceExists ($interfaces); $code = self::generateClass ($interfaces); return eval($code); }
protected static function generateClass(array $interfaces) { $interfaceList = implode(",", $interfaces); $functionList = ""; foreach ($interfaces as $interface) { $class = new ReflectionClass ($interface); $methods = $class->getMethods(); foreach ($methods as $method){ $parameters = []; foreach ($method->getParameters() as $parameter){ $parameters[] = "$" . $parameter->getName(); } $functionList .= sprintf( self::FUNCTION_TEMPLATE, $method->getName(), implode( ",", $parameters ) ); } } return sprintf ( self::CLASS_TEMPLATE, $interfaceList, $functionList ); }
其中generateClass就是通過反射掃描接口方法,然后根據 stub 模板生成方法拼接成代碼,最后通過 eval 執行。
2.測試
interface Test1{ public function t1(); } interface Test2{ public function t2(); } class TestImpl implements Test1,Test2{ public function t1(){ echo "t1"; } public function t2(){ echo "t2"; } } $impl = new TestImpl(); class Handler implements InvocationHandler { private $target; public function __construct($impl){ $this->target = $impl; } function invoke(ReflectionMethod $method, array $arr_args){ echo "前置操作"; $method->invokeArgs($this->target, $arr_args); echo "后置操作"; } } $proxy = Proxy::newProxyInstance($impl, ["Test1", "Test2"], new Handler($impl)); $proxy->t1();
輸出:
前置操作 t1 后置操作依賴注入
依賴注入是現代化框架中非常常見的一個功能,它必須和服務容器結合使用。用過 Laravel 框架的童鞋應該很熟悉,我們可以在任意需要服務的地方通過類型提示聲明,運行時框架就會自動幫我們注入所需要的對象。以 Laravel 框架的源碼簡單解析下:
在 Laravel 框架中,我們解析一個對象的方法可以這樣:
$obj = App::make(ClassName);
make方法實際上底層也是調用了IlluminateContainerContaiern::build($concrete)這個方法,整理一下源碼就是:
public function build($concrete){ $reflector = new ReflectionClass($concrete); $constructor = $reflector->getConstructor(); if (is_null($constructor)) { return new $concrete; } $dependencies = $constructor->getParameters(); $instances = $this->resolveDependencies($dependencies); return $reflector->newInstanceArgs($instances); }
實際代碼很簡單,反射類獲取構造方法,然后解析依賴參數,傳入執行。
歡迎關我的個人公眾號:左手代碼
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/26314.html
摘要:發現大量的使用了反射機制。下面就來簡單看看一些反射的應用獲得反射下面我們來通過這個反射來得到的私有屬性得到結果得到這樣我們就可以很輕松的獲得的私有屬性了。最后通過執行該方法反射還有很多可用的方法,這里就不一一說了。 這幾天在看laravel框架的核心代碼。發現大量的使用了反射機制。下面就來簡單看看一些反射的應用 class A { private $_foo = this i...
摘要:控制反轉容器控制反轉使依賴注入變得更加便捷。有瑕疵控制反轉容器是實現的控制翻轉容器的一種替代方案。容器的獨立使用即使沒有使用框架,我們仍然可以在項目中使用安裝組件來使用的控制反轉容器。在沒有給定任何信息的情況下,容器是無法實例化相關依賴的。 聲明:本文并非博主原創,而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當然也不是原汁原味...
摘要:現代的面向對象的思想不強調為真實世界建模變得更加理性化一些,把目標放在解耦上。各種語言中的多態多態確切的含義是同一類的對象收到相同消息時,會得到不同的結果。小結本章主要介紹面向對象思想的程序的組成元素類和對象。 第一章 面向對象思想的核心概念 showImg(https://segmentfault.com/img/bVNfjM?w=673&h=334); showImg(https:...
摘要:反射簡介參考官方簡介的話,具有完整的反射,添加了對類接口函數方法和擴展進行反向工程的能力。此外,反射提供了方法來取出函數類和方法中的文檔注釋。 反射簡介 參考官方簡介的話,PHP 5 具有完整的反射 API,添加了對類、接口、函數、方法和擴展進行反向工程的能力。 此外,反射 API 提供了方法來取出函數、類和方法中的文檔注釋。 YII2框架中示例 對于yii2框架,應該都知道di容器,...
閱讀 2917·2021-11-24 09:39
閱讀 1175·2021-11-02 14:38
閱讀 4173·2021-09-10 11:26
閱讀 2761·2021-08-25 09:40
閱讀 2319·2019-08-30 15:54
閱讀 491·2019-08-30 10:56
閱讀 2757·2019-08-26 12:14
閱讀 3227·2019-08-26 12:13