摘要:反射簡(jiǎn)介參考官方簡(jiǎn)介的話,具有完整的反射,添加了對(duì)類接口函數(shù)方法和擴(kuò)展進(jìn)行反向工程的能力。此外,反射提供了方法來(lái)取出函數(shù)類和方法中的文檔注釋。
反射簡(jiǎn)介
參考官方簡(jiǎn)介的話,PHP 5 具有完整的反射 API,添加了對(duì)類、接口、函數(shù)、方法和擴(kuò)展進(jìn)行反向工程的能力。 此外,反射 API 提供了方法來(lái)取出函數(shù)、類和方法中的文檔注釋。
YII2框架中示例對(duì)于yii2框架,應(yīng)該都知道di容器,對(duì)于di容器的源碼這里也主要講明Container類,先看看平時(shí)怎么使用di,就用yii2框架中注釋的示例代碼來(lái)展示;
container調(diào)用示例namespace appmodels; use yiiaseBaseObject; use yiidbConnection; use yiidiContainer; interface UserFinderInterface { function findUser(); } class UserFinder extends BaseObject implements UserFinderInterface { public $db; public function __construct(Connection $db, $config = []) { $this->db = $db; parent::__construct($config); } public function findUser() { } } class UserLister extends BaseObject { public $finder; public function __construct(UserFinderInterface $finder, $config = []) { $this->finder = $finder; parent::__construct($config); } } $container = new Container; $container->set("yiidbConnection", [ "dsn" => "...", ]); $container->set("appmodelsUserFinderInterface", [ "class" => "appmodelsUserFinder", ]); $container->set("userLister", "appmodelsUserLister"); $lister = $container->get("userLister"); // 上述操作相當(dāng)于下列實(shí)現(xiàn) $db = new yiidbConnection(["dsn" => "..."]); $finder = new UserFinder($db); $lister = new UserLister($finder);
上面的示例代碼只是實(shí)例化了Container類,然后調(diào)用set方法注入了其他對(duì)象,最后獲取到了依賴與其他對(duì)象創(chuàng)建的lister對(duì)象,既然只調(diào)用了set方法與get方法,那就先從調(diào)用最多的set開始看Container代碼。
set方法public function set($class, $definition = [], array $params = []) { $this->_definitions[$class] = $this->normalizeDefinition($class, $definition); $this->_params[$class] = $params; unset($this->_singletons[$class]); return $this; }
上面的代碼比較簡(jiǎn)潔,調(diào)用了類的normalizeDefinition方法,這個(gè)一會(huì)再說(shuō),先說(shuō)明在該方法中出現(xiàn)的三個(gè)屬性的含義
_definitions數(shù)組,保存依賴定義
_params數(shù)組,保存構(gòu)造函數(shù)的參數(shù)
_singletons,保存單例
再看normalizeDefinition方法,該方法主要作用是規(guī)范類定義
protected function normalizeDefinition($class, $definition) { if (empty($definition)) { // 為空 return ["class" => $class]; } elseif (is_string($definition)) { // 為字符串 return ["class" => $definition]; } elseif (is_callable($definition, true) || is_object($definition)) { // 檢驗(yàn)是否為可調(diào)用函數(shù)或者對(duì)象 return $definition; } elseif (is_array($definition)) { // 檢測(cè)是否為數(shù)組 if (!isset($definition["class"])) { if (strpos($class, "") !== false) { $definition["class"] = $class; } else { throw new InvalidConfigException("A class definition requires a "class" member."); } } return $definition; } throw new InvalidConfigException("Unsupported definition type for "$class": " . gettype($definition)); }
上述代碼中已做了一些判斷注釋,不難發(fā)現(xiàn)最后需要返回的definition變量需要為數(shù)組格式,或者可調(diào)用函數(shù)與對(duì)象,注意回到剛開始的調(diào)用示例代碼,definition變量分別有數(shù)組格式不帶class鍵,
數(shù)組格式帶class鍵,與字符串類型。到底set方法調(diào)用已完畢,從源碼中分析基本上看不到反射的影子,也就是些傳入?yún)?shù)格式兼容處理再寫入類屬性,接著來(lái)看下示例代碼中的get方法吧。
public function get($class, $params = [], $config = []) { if (isset($this->_singletons[$class])) { // 直接返回單例 return $this->_singletons[$class]; } elseif (!isset($this->_definitions[$class])) { // 調(diào)用bulid return $this->build($class, $params, $config); } $definition = $this->_definitions[$class]; if (is_callable($definition, true)) { // 可調(diào)用函數(shù)情況 $params = $this->resolveDependencies($this->mergeParams($class, $params)); $object = call_user_func($definition, $this, $params, $config); } elseif (is_array($definition)) { // 數(shù)組 $concrete = $definition["class"]; unset($definition["class"]); $config = array_merge($definition, $config); $params = $this->mergeParams($class, $params); if ($concrete === $class) { $object = $this->build($class, $params, $config); } else { $object = $this->get($concrete, $params, $config); } } elseif (is_object($definition)) { // 對(duì)象直接保存到單例屬性集合中去 return $this->_singletons[$class] = $definition; } else { throw new InvalidConfigException("Unexpected object definition type: " . gettype($definition)); } if (array_key_exists($class, $this->_singletons)) { // singleton $this->_singletons[$class] = $object; } return $object; }
上述代碼,簡(jiǎn)要?jiǎng)澐忠幌?,?qǐng)稍作瀏覽,后面會(huì)繼續(xù)講述,先說(shuō)明屬性_definitions集合中不存在的情況,即調(diào)用build,這個(gè)一會(huì)說(shuō)明,再看如果存在相關(guān)class鍵的情況,下面會(huì)做幾種情況的處理,
可調(diào)用函數(shù)情況下,調(diào)用resolveDependencies方法,再call_user_func調(diào)用函數(shù)
數(shù)組情況下,獲取值與class比較,相等的情況去調(diào)用build方法,不想等重新調(diào)用get方法使用該值
為對(duì)象的化直接存儲(chǔ)到_singletons屬性集合中去,并直接返回對(duì)象,這個(gè)不作贅述
下面分別來(lái)簡(jiǎn)要分析一下上述調(diào)用的幾個(gè)方法,bulid與resolveDependencies方法
bulid方法的調(diào)用邏輯先看下build方法調(diào)用源碼
protected function build($class, $params, $config) { // 聲明變量分別存儲(chǔ)getDependencies方法返回的數(shù)組 list($reflection, $dependencies) = $this->getDependencies($class); // 將params數(shù)組的數(shù)據(jù)mergy并覆蓋入變量$dependencies foreach ($params as $index => $param) { $dependencies[$index] = $param; } // 調(diào)用resolveDependencies方法 $dependencies = $this->resolveDependencies($dependencies, $reflection); // 調(diào)用反射類方法,檢測(cè)類是否可實(shí)例化 if (!$reflection->isInstantiable()) { throw new NotInstantiableException($reflection->name); } if (empty($config)) { // 創(chuàng)建一個(gè)類的新實(shí)例,變量$dependencies作為參數(shù)將傳遞到類的構(gòu)造函數(shù)。 return $reflection->newInstanceArgs($dependencies); } $config = $this->resolveDependencies($config); // 如果變量$dependencies為空并且class是yiiaseConfigurable接口的實(shí)現(xiàn) if (!empty($dependencies) && $reflection->implementsInterface("yiiaseConfigurable")) { // set $config as the last parameter (existing one will be overwritten) $dependencies[count($dependencies) - 1] = $config; return $reflection->newInstanceArgs($dependencies); } // 創(chuàng)建對(duì)象,注入?yún)?shù) $object = $reflection->newInstanceArgs($dependencies); // 對(duì)象屬性賦值 foreach ($config as $name => $value) { $object->$name = $value; } return $object; }
看了上述源碼,也基本了解此方法是為了返回實(shí)例化對(duì)象,并調(diào)用了反射的一些接口函數(shù),這里基本上可以知道反射的一些作用,第一個(gè)就是檢測(cè)類的合法性,例如檢測(cè)是否為接口實(shí)現(xiàn),是否可實(shí)例化,
還有一個(gè)就是創(chuàng)造,上述可以看出根據(jù)反射創(chuàng)建類的實(shí)例,并注入構(gòu)造函數(shù)依賴的參數(shù)。下面再了解下該方法里面調(diào)用的兩個(gè)依賴方法,分別為開頭的變量聲明getDependencies與resolveDependencies
處理變量。
protected function getDependencies($class) { // 檢測(cè)是否已存在該反射 if (isset($this->_reflections[$class])) { return [$this->_reflections[$class], $this->_dependencies[$class]]; } $dependencies = []; $reflection = new ReflectionClass($class); // 反射對(duì)應(yīng)類的信息 $constructor = $reflection->getConstructor(); // 獲取類的構(gòu)造函數(shù) if ($constructor !== null) { // 如果構(gòu)造函數(shù)不為空,獲取構(gòu)造函數(shù)中的參數(shù)循環(huán)處理 foreach ($constructor->getParameters() as $param) { if (version_compare(PHP_VERSION, "5.6.0", ">=") && $param->isVariadic()) { // 檢測(cè)php版本與構(gòu)造參數(shù)檢測(cè)是否為可變參數(shù) break; } elseif ($param->isDefaultValueAvailable()) { // 檢測(cè)參數(shù)是否是否有默認(rèn)值,如果有數(shù)據(jù)保存默認(rèn)值 $dependencies[] = $param->getDefaultValue(); } else { // 獲取參數(shù)的類型提示符,查看是否為null,返回的是reflectClass對(duì)象 // 這里再舉個(gè)例子,例如構(gòu)造函數(shù)為這樣__construct(Db $db);這里返回的就是Db類的反射 $c = $param->getClass(); // 創(chuàng)建Instance實(shí)例存儲(chǔ)類名 $dependencies[] = Instance::of($c === null ? null : $c->getName()); } } } // 存儲(chǔ)起來(lái) $this->_reflections[$class] = $reflection; $this->_dependencies[$class] = $dependencies; return [$reflection, $dependencies]; }
該方法主要作用為解析依賴信息,主要是獲取類的構(gòu)造函數(shù)的信息,這樣才能調(diào)用構(gòu)造函數(shù)創(chuàng)建實(shí)例。
resilveDependencies方法調(diào)用該方法主要是實(shí)例化依賴,也就是創(chuàng)建構(gòu)造函數(shù)的參數(shù)對(duì)象,不作過(guò)多贅述
protected function resolveDependencies($dependencies, $reflection = null) { foreach ($dependencies as $index => $dependency) { // 在解析依賴信息的getDependencies中,有部分參數(shù)沒(méi)有默認(rèn)值,而是創(chuàng)建了Instance對(duì)象 // 這里會(huì)將這些Instance對(duì)象實(shí)例化對(duì)真正的構(gòu)造函數(shù)的參數(shù)對(duì)象 if ($dependency instanceof Instance) { if ($dependency->id !== null) { $dependencies[$index] = $this->get($dependency->id); } elseif ($reflection !== null) { $name = $reflection->getConstructor()->getParameters()[$index]->getName(); $class = $reflection->getName(); throw new InvalidConfigException("Missing required parameter "$name" when instantiating "$class"."); } } } return $dependencies; }總結(jié)
在上述源碼中基本上可以看到幾處反射的應(yīng)用,而反射到底是什么,由什么作用呢?想必看完上文也會(huì)有一點(diǎn)點(diǎn)理解,嗯,其實(shí)意義如其名,
就是反射類的信息,其作用是獲取類的信息,而php的反射類也提供了很多的接口函數(shù)以供使用,使用的時(shí)候可以去查詢官網(wǎng)手冊(cè)。
上文也看出來(lái)yii2框架中的di容器創(chuàng)建對(duì)象,在這里還是希望可以稍微講述下剛開始的示例代碼,其先在容器中注入了數(shù)據(jù)庫(kù)連接類,finder類,listener類,而finder類構(gòu)造函數(shù)依賴于
數(shù)據(jù)庫(kù)連接類,listener類依賴與finder類,由獲取依賴信息方法可以知道構(gòu)造中會(huì)去取出依賴對(duì)象信息然后調(diào)用解析依賴信息重新去調(diào)用get方法返回實(shí)例化對(duì)象實(shí)現(xiàn)其中的注入關(guān)系。
個(gè)人博客地址
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/29040.html
摘要:構(gòu)造器注入實(shí)現(xiàn)特定參數(shù)的構(gòu)造函數(shù),在新建對(duì)象時(shí)傳入所依賴類型的對(duì)象。 基本概念 1.依賴倒置(反轉(zhuǎn))原則(DIP):一種軟件架構(gòu)設(shè)計(jì)的原則(抽象概念,是一種思想)在面向?qū)ο缶幊填I(lǐng)域中,依賴反轉(zhuǎn)原則(Dependency inversion principle,DIP)是指一種特定的解耦(傳統(tǒng)的依賴關(guān)系創(chuàng)建在高層次上,而具體的策略設(shè)置則應(yīng)用在低層次的模塊上)形式,使得高層次的模塊不依賴于...
摘要:在中使用解耦,有兩種注入方式構(gòu)造函數(shù)注入屬性注入。對(duì)象的實(shí)例化解析依賴信息該方法實(shí)質(zhì)上就是通過(guò)的反射機(jī)制,通過(guò)類的構(gòu)造函數(shù)的參數(shù)分析他所依賴的單元。 有關(guān)概念 依賴倒置原則(Dependence Inversion Principle, DIP) 傳統(tǒng)軟件設(shè)計(jì)中,上層代碼依賴于下層代碼,當(dāng)下層出現(xiàn)變動(dòng)時(shí),上層也要相應(yīng)變化。 DIP的核心思想是:上層定義接口,下層實(shí)現(xiàn)這個(gè)接口,從而使的下...
摘要:簡(jiǎn)單來(lái)說(shuō),是一個(gè)輕量級(jí)的控制反轉(zhuǎn)和面向切面的容器框架。變成的支持提供面向切面編程,可以方便的實(shí)現(xiàn)對(duì)程序進(jìn)行權(quán)限攔截,運(yùn)行監(jiān)控等功能。用于反射創(chuàng)建對(duì)象,默認(rèn)情況下調(diào)用無(wú)參構(gòu)造函數(shù)。指定對(duì)象的作用范圍。 1.Spring介紹 1.1 Spring概述 Spring是一個(gè)開源框架,Spring是于2003 年興起的一個(gè)輕量級(jí)的Java 開發(fā)框架,由Rod Johnson 在其著作Expert...
摘要:從使用到原理學(xué)習(xí)線程池關(guān)于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實(shí)現(xiàn)在軟件開發(fā)中,分散于應(yīng)用中多出的功能被稱為橫切關(guān)注點(diǎn)如事務(wù)安全緩存等。 Java 程序媛手把手教你設(shè)計(jì)模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經(jīng)風(fēng)雨慢慢變老,回首走過(guò)的點(diǎn)點(diǎn)滴滴,依然清楚的記得當(dāng)初愛(ài)情萌芽的模樣…… Java 進(jìn)階面試問(wèn)題列表 -...
閱讀 2164·2021-11-22 15:24
閱讀 2469·2021-09-09 11:53
閱讀 3079·2021-09-04 16:40
閱讀 1670·2019-08-30 15:52
閱讀 3386·2019-08-29 13:47
閱讀 2772·2019-08-26 17:40
閱讀 1586·2019-08-26 13:24
閱讀 2275·2019-08-26 12:01