摘要:反射在每個(gè)面向?qū)ο蟮木幊陶Z言中都存在,它的主要目的就是在運(yùn)行時(shí)分析類或者對(duì)象的狀態(tài),導(dǎo)出或提取出關(guān)于類方法屬性參數(shù)等的詳細(xì)信息,包括注釋。反射是操縱面向?qū)ο蠓缎椭性P偷模捎糜跇?gòu)建復(fù)雜,可擴(kuò)展的應(yīng)用。
反射在每個(gè)面向?qū)ο蟮木幊陶Z言中都存在,它的主要目的就是在運(yùn)行時(shí)分析類或者對(duì)象的狀態(tài),導(dǎo)出或提取出關(guān)于類、方法、屬性、參數(shù)等的詳細(xì)信息,包括注釋。 反射是操縱面向?qū)ο蠓缎椭性P偷?API,可用于構(gòu)建復(fù)雜,可擴(kuò)展的應(yīng)用。反射在日常的 Web 開發(fā)中其實(shí)用的不多,更多的是在偏向底層一些的代碼中,比如說框架的底層中依賴注入、對(duì)象池、動(dòng)態(tài)代理、自動(dòng)獲取插件列表、自動(dòng)生成文檔以及一些設(shè)計(jì)模式等等,都會(huì)大量運(yùn)用到反射技術(shù)。
PHP 的反射 API 很多,但是常用的一般都是 ReflectionClass 和 ReflectionMethod:
1.ReflectionClass
這個(gè)是用來獲取類的信息,可以簡單測(cè)試一下:
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());
返回的是一個(gè) ReflectionMethod 的數(shù)組:
array(1) { [0]=> object(ReflectionMethod)#2 (2) { ["name"]=> string(7) "setName" ["class"]=> string(7) "Student" } }
附上一些常用方法,詳細(xì)的可以查看文檔:
ReflectionClass::getMethods 獲取方法的數(shù)組 ReflectionClass::getName 獲取類名 ReflectionClass::hasMethod 檢查方法是否已定義 ReflectionClass::hasProperty 檢查屬性是否已定義 ReflectionClass::isAbstract 檢查類是否是抽象類(abstract) ReflectionClass::isFinal 檢查類是否聲明為 final ReflectionClass::isInstantiable 檢查類是否可實(shí)例化 ReflectionClass::newInstance 從指定的參數(shù)創(chuàng)建一個(gè)新的類實(shí)例
2.ReflectionMethod
這個(gè)主要是針對(duì)方法的反射,我們可以簡單執(zhí)行一下:
$stu = new Student(); $ref = new ReflectionClass(Student::class); $method = $ref->getMethod("setName"); $method->invoke($stu, "john"); var_dump($stu->name);
可以輸出:
john
附上一些常用的方法,詳細(xì)的可以去看看文檔:
ReflectionMethod::invoke 執(zhí)行 ReflectionMethod::invokeArgs 帶參數(shù)執(zhí)行 ReflectionMethod::isAbstract 判斷方法是否是抽象方法 ReflectionMethod::isConstructor 判斷方法是否是構(gòu)造方法 ReflectionMethod::isDestructor 判斷方法是否是析構(gòu)方法 ReflectionMethod::isFinal 判斷方法是否定義 final ReflectionMethod::isPrivate 判斷方法是否是私有方法 ReflectionMethod::isProtected 判斷方法是否是保護(hù)方法 (protected) ReflectionMethod::isPublic 判斷方法是否是公開方法 ReflectionMethod::isStatic 判斷方法是否是靜態(tài)方法 ReflectionMethod::setAccessible 設(shè)置方法是否訪問
接下來說一些反射在實(shí)際開發(fā)中比較常見的應(yīng)用。
執(zhí)行私有方法其實(shí)反射不僅可以執(zhí)行私有方法,還可以讀取私有屬性。這個(gè)主要應(yīng)用在一些設(shè)計(jì)不合理的 SDK 里面,一些很好用的方法和屬性卻不對(duì)外開放。
class Student { private $name; private function setName($name) { $this->name = $name; } }
執(zhí)行私有方法:
$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);動(dòng)態(tài)代理
其實(shí) PHP 有魔術(shù)方法,所以實(shí)現(xiàn)動(dòng)態(tài)代理已經(jīng)很簡單了,但是通過魔術(shù)方法來實(shí)現(xiàn)的都不完美,個(gè)人理解最好的實(shí)現(xiàn)應(yīng)該還是 JDK 中的動(dòng)態(tài)代理,基于一個(gè)接口進(jìn)行掃描實(shí)現(xiàn)實(shí)在 PHP 中也可以實(shí)現(xiàn)。我們先來看看動(dòng)態(tài)代理在 JDK 中是怎么使用的:
1.首先定義一個(gè)實(shí)現(xiàn)類的接口,JDK 的動(dòng)態(tài)代理必須基于接口(Cglib則不用)
package com.yao.proxy; public interface Helloworld { void sayHello(); }
2.定義一個(gè)實(shí)現(xiàn)類,這個(gè)類就是要被代理的對(duì)象
package com.yao.proxy; import com.yao.HelloWorld; public class HelloworldImpl implements HelloWorld { public void sayHello() { System.out.print("hello world"); } }
3.調(diào)用被代理對(duì)象方法的實(shí)現(xiàn)類
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.測(cè)試
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 的動(dòng)態(tài)代理在底層實(shí)際上是掃描實(shí)現(xiàn)的接口,然后動(dòng)態(tài)生成類的字節(jié)碼文件。PHP 是動(dòng)態(tài)語言,所以可以用 eval 來實(shí)現(xiàn)。
1.定義調(diào)度器接口
interface InvocationHandler { function invoke($method, array $arr_args); }
2.動(dòng)態(tài)代理實(shí)現(xiàn)
定義一個(gè)類的 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 };
定義一個(gè)方法的 stub:
public function %s(%s) { $args = func_get_args(); $method = explode("::", __METHOD__); $this->handler->invoke(new ReflectionMethod($this->target, $method[1]), $args); }
Proxy 實(shí)現(xiàn):
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就是通過反射掃描接口方法,然后根據(jù) stub 模板生成方法拼接成代碼,最后通過 eval 執(zhí)行。
2.測(cè)試
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 后置操作依賴注入
依賴注入是現(xiàn)代化框架中非常常見的一個(gè)功能,它必須和服務(wù)容器結(jié)合使用。用過 Laravel 框架的童鞋應(yīng)該很熟悉,我們可以在任意需要服務(wù)的地方通過類型提示聲明,運(yùn)行時(shí)框架就會(huì)自動(dòng)幫我們注入所需要的對(duì)象。以 Laravel 框架的源碼簡單解析下:
在 Laravel 框架中,我們解析一個(gè)對(duì)象的方法可以這樣:
$obj = App::make(ClassName);
make方法實(shí)際上底層也是調(diào)用了IlluminateContainerContaiern::build($concrete)這個(gè)方法,整理一下源碼就是:
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); }
實(shí)際代碼很簡單,反射類獲取構(gòu)造方法,然后解析依賴參數(shù),傳入執(zhí)行。
歡迎關(guān)我的個(gè)人公眾號(hào):左手代碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/26314.html
摘要:發(fā)現(xiàn)大量的使用了反射機(jī)制。下面就來簡單看看一些反射的應(yīng)用獲得反射下面我們來通過這個(gè)反射來得到的私有屬性得到結(jié)果得到這樣我們就可以很輕松的獲得的私有屬性了。最后通過執(zhí)行該方法反射還有很多可用的方法,這里就不一一說了。 這幾天在看laravel框架的核心代碼。發(fā)現(xiàn)大量的使用了反射機(jī)制。下面就來簡單看看一些反射的應(yīng)用 class A { private $_foo = this i...
摘要:反射機(jī)制反射機(jī)制從開始支持,做業(yè)務(wù)開發(fā)的話應(yīng)該很少接觸反射。我的理解就是反射機(jī)制能拿到類里面的屬性方法,和的也可以以上是官方文檔中給出的東西,說實(shí)話我看了感覺沒什么感覺。在容器成員變量中數(shù)組維護(hù)這個(gè)類,反射實(shí)例調(diào)用構(gòu)造函數(shù),獲取返回值。 PHP反射機(jī)制 PHP反射機(jī)制從PHP5開始支持,做業(yè)務(wù)開發(fā)的話應(yīng)該很少接觸反射。我其實(shí)也是接觸不多,最近在學(xué)習(xí)laravel的優(yōu)雅,就接觸了到它其中...
摘要:控制反轉(zhuǎn)容器控制反轉(zhuǎn)使依賴注入變得更加便捷。有瑕疵控制反轉(zhuǎn)容器是實(shí)現(xiàn)的控制翻轉(zhuǎn)容器的一種替代方案。容器的獨(dú)立使用即使沒有使用框架,我們?nèi)匀豢梢栽陧?xiàng)目中使用安裝組件來使用的控制反轉(zhuǎn)容器。在沒有給定任何信息的情況下,容器是無法實(shí)例化相關(guān)依賴的。 聲明:本文并非博主原創(chuàng),而是來自對(duì)《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味...
摘要:現(xiàn)代的面向?qū)ο蟮乃枷氩粡?qiáng)調(diào)為真實(shí)世界建模變得更加理性化一些,把目標(biāo)放在解耦上。各種語言中的多態(tài)多態(tài)確切的含義是同一類的對(duì)象收到相同消息時(shí),會(huì)得到不同的結(jié)果。小結(jié)本章主要介紹面向?qū)ο笏枷氲某绦虻慕M成元素類和對(duì)象。 第一章 面向?qū)ο笏枷氲暮诵母拍?showImg(https://segmentfault.com/img/bVNfjM?w=673&h=334); showImg(https:...
摘要:反射簡介參考官方簡介的話,具有完整的反射,添加了對(duì)類接口函數(shù)方法和擴(kuò)展進(jìn)行反向工程的能力。此外,反射提供了方法來取出函數(shù)類和方法中的文檔注釋。 反射簡介 參考官方簡介的話,PHP 5 具有完整的反射 API,添加了對(duì)類、接口、函數(shù)、方法和擴(kuò)展進(jìn)行反向工程的能力。 此外,反射 API 提供了方法來取出函數(shù)、類和方法中的文檔注釋。 YII2框架中示例 對(duì)于yii2框架,應(yīng)該都知道di容器,...
閱讀 2895·2021-11-24 09:39
閱讀 1157·2021-11-02 14:38
閱讀 4141·2021-09-10 11:26
閱讀 2743·2021-08-25 09:40
閱讀 2303·2019-08-30 15:54
閱讀 477·2019-08-30 10:56
閱讀 2738·2019-08-26 12:14
閱讀 3211·2019-08-26 12:13