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