第2章 解析框架 IOC 與 DI 實(shí)現(xiàn)原理
2.1 理解PHP反射
1. 什么是反射 ?
反射是指程序可以訪問、檢測(cè)和修改它本身狀態(tài)或行為的一種能力。 反射可以做什么 : 獲取類型的相關(guān)信息
動(dòng)態(tài)調(diào)用方法
動(dòng)態(tài)構(gòu)造對(duì)象
從程序集中獲得類型
2. 怎么實(shí)現(xiàn)反射?
可通過反射類 ( Reflection ) 或者 內(nèi)省 ( Introspection ) 來完成對(duì)自己本身的狀態(tài)檢查和修改。
那反射和內(nèi)省有啥區(qū)別,前者是 PHP 官方提供的一個(gè)反射類,實(shí)例化反射類就可做到對(duì)自身狀態(tài)的修改,后者則是根據(jù) PHP 提供的相關(guān)操作方法來完成本身的狀態(tài)修改。
Introspection 具有操作方法如下 :
class_exists()
method_exists()
property_exists() //檢查對(duì)象或類是否具有該屬性
trait_exists() //檢查指定的 trait 是否存在
class_alias() //為一個(gè)類創(chuàng)建別名
get_class()
get_parent_class() //返回對(duì)象或類的父類名
get_called_class() //獲取靜態(tài)方法調(diào)用的類名。
get_class_methods()//取得class name 類的所有的方法名,并且組成一個(gè)數(shù)組
get_class_vars() // 返回由類的默認(rèn)屬性組成的數(shù)組
get_object_vars() //返回一個(gè)數(shù)組。獲取$object對(duì)象中的屬性,組成一個(gè)數(shù)組
is_subclass_of()?// 判斷一個(gè)對(duì)象是否為一個(gè)類的子類
is_a() //某對(duì)象是否屬于該類 或 該類是此對(duì)象的父類
案例測(cè)試代碼如下 :
";
???echo "parent是誰(shuí):" . get_parent_class($this) , "
";
?}
}
?
if (class_exists("Introspection")) {
???$introspection = new Introspection();
???echo "類名是: " . get_class($introspection) . "是父類"."
";
???$introspection->description();
}
?
if (class_exists("Nahida")) {
???$nahida = new Nahida();
???$nahida->description();
?
??if (is_subclass_of($nahida, "Introspection")) {
???echo "Yes, " . get_class($nahida) . "是Introspection的子類";
?}
??else {
???echo "No, " . get_class($nahida) . " 不是Introspection的子類";
?}
}
Reflector具有參數(shù)如下
:
name = $name; ? $this->url = $url; } public function getName(){ ? return $this->name; } ??/** ??* 獲取連接 ??* @return [type] [description] ??*/ ??private function getUrl(){ ? return $this->url; } } ?> -------------------------分割線------------------------------ getProperties(); // 以數(shù)組的形式返回 Nahida 類的所有屬性 $property?= $class->getProperty('name');?// 獲取 Nahida 類的 name 屬性 $methods??= $class->getMethods();?// 以數(shù)組的形式返回 Nahida 類的所有方法 $method??= $class->getMethod('getName'); // 獲取 Nahida 類的 getName 方法 $constants?= $class->getConstants(); // 以數(shù)組的形式獲取所有常量 $constant?= $class->getConstant('TITLE'); // 獲取 TITLE 常量 $namespace???= $class->getNamespaceName();?// 獲取類的命名空間 $comment_class?= $class->getDocComment();???// 獲取 Nahida 類的注釋文檔,即定義在類之前的注釋 $comment_method = $class->getMethod('getUrl')->getDocComment();?// 獲取 Website 類中 getUrl 方法的注釋文檔 ?>? 2.2 理解 IOC IOC 是Inversion of Control 的縮寫, 即 “控制反轉(zhuǎn)",它是一種設(shè)計(jì)思想。 IOC 意味著將類實(shí)例化的工作交給容器來完成,而不是傳統(tǒng)的在你的對(duì)象內(nèi)部直接進(jìn)行實(shí)例化。然后在調(diào)用對(duì)象的方法。 如何理解 IOC 呢 ? 首先需要明確誰(shuí)控制誰(shuí),控制什么,什么是反轉(zhuǎn)(有反轉(zhuǎn)就應(yīng)該有正轉(zhuǎn)了),在哪些反轉(zhuǎn)了 ?
誰(shuí)控制誰(shuí),控制什么:
傳統(tǒng) PHP 程序開發(fā)中,我們是直接在對(duì)象內(nèi)部通過 new 進(jìn)行創(chuàng)建對(duì)象,是程序主動(dòng)去創(chuàng)建依賴對(duì)象;這就是正轉(zhuǎn)。而 IOC 是有專門的一個(gè)容器來創(chuàng)建這些對(duì)象,即由 IOC 容器來控制對(duì) 象的創(chuàng)建。
那誰(shuí)控制誰(shuí):
就是 IOC 容器控制了對(duì)象。
控制了什么:
容器主要控制了外部資源獲?。ú恢皇菍?duì)象,包括比如文件等)。
為何是反轉(zhuǎn),哪些方面反轉(zhuǎn)了:
反轉(zhuǎn)是由容器到應(yīng)用程序中創(chuàng)建及注入依賴對(duì)象。
什么是反轉(zhuǎn):
因?yàn)橛扇萜鲙臀覀儾檎壹白⑷胍蕾噷?duì)象,應(yīng)用程序只是被動(dòng)的接受依賴對(duì)象,所以是反轉(zhuǎn)。
哪里反轉(zhuǎn)了:
依賴對(duì)象的獲取被反轉(zhuǎn)了。
2.3 理解 DI
DI
( Dependency Injection) 即 “依賴注入” :組件之間的依賴關(guān)系由容器動(dòng)態(tài)的將某個(gè)類與類的依賴關(guān)系注入到組件之中。依賴注入的目的就是為了提升組件重用的頻率,并為系統(tǒng)搭建一個(gè)靈活、可擴(kuò)展的平臺(tái)。通過依賴注入機(jī)制,只需要通過簡(jiǎn)單的配置、注入方法接口,就可指定目標(biāo)需要的資源,完成自身的業(yè)務(wù)邏輯,而不需要關(guān)心具體的資源來自何處,由誰(shuí)實(shí)現(xiàn)。 怎么理解 DI ? “誰(shuí)依賴誰(shuí),為什么需要依賴,誰(shuí)注入誰(shuí),注入了什么” ● 誰(shuí)依賴于誰(shuí):應(yīng)用程序(框架開發(fā)的業(yè)務(wù)對(duì)象)依賴于 IOC 容器。 ● 為什么需要依賴:應(yīng)用程序需要 IOC 容器來提供對(duì)象需要的外部資源。 ● 誰(shuí)注入誰(shuí):IOC 容器注入了應(yīng)用程序依賴的某個(gè)對(duì)象。 ● 注入了什么:就是注入某個(gè)對(duì)象所需要的外部資源(包括對(duì)象、資源、常量數(shù)據(jù))。 IOC 和 DI 有什么關(guān)系呢? 其實(shí)它們是同一個(gè)概念的不同角度描述,由于控制反轉(zhuǎn)概念比較含糊(可能只是理解為容器控制對(duì)象這一個(gè)層面,很難讓人想到誰(shuí)來維護(hù)對(duì)象關(guān)系),所以 2004 年大師級(jí)人物 Martin Fowler 又給出了一個(gè)新的名字:“依賴注入”,相對(duì) IOC 而言,“依賴注入”明確描述了“被注入對(duì)象依賴 IOC 容器配置依賴對(duì)象 ”。 2.4 從零實(shí)現(xiàn) IOC
如何實(shí)現(xiàn) IOC 容器 ?
按照上面所訴,需要一個(gè)容器來存放依賴對(duì)象,同時(shí)需要一個(gè)依賴注入的方法,好讓外面的內(nèi)容可注入到對(duì)象中。同時(shí)容器自身需要提供獲取依賴對(duì)象的方法。 我喜歡的類型 ??public function __construct(女和男 $御女和文雅) ?{ ????$this->r = $御女和文雅; ?} ??// 談戀愛 ??public function 一起吃飯() ?{ return '和'.$this->r->name.'一起吃飯'; ?} ??public function 一起看電影() ?{ return '和'.$this->r->name.'一起看電影'; ?} ??public function f一起去玩() ?{ ????return '和'.$this->r->name.'一起去玩'; ?} } ------------------------------------------------------------ class 摳腳大漢 { ?public $name = '摳腳大漢'; } $摳腳大漢 = new 摳腳大漢(); ? interface 女和男 { } ? class 御女和文雅 implements 女和男 { ??public $name = '御女和文雅'; } ? class 蘿莉和文雅 implements 女和男 { ??public $name = '蘿莉和文雅'; } ? class 女神和文雅 implements 女和男 { ??public $name = '女神和文雅'; } ? $御女和文雅 = new 御女和文雅(); ? $單身 = new 單身同胞($御女和文雅); ? echo $單身->f一起去玩(); ? // ioc容器 為什么需要呢? /** * 相親平臺(tái) : 記錄各種用戶的信息 */ class 相親平臺(tái) {?// $bindings ??private $信息 = []; ??// $resolved ??private $相親成功 = []; ??// $instances ??private $共享對(duì)象 = []; ??// $aliases / $abstractAliases ??private $網(wǎng)名 = []; ? ??public function 登記注入($類型, $詳細(xì)信息) ?{ $this->信息[$類型] = $詳細(xì)信息; ?} ??public function 網(wǎng)名登記注入($類型, $詳細(xì)信息) ?{ $this->信息[$類型] = $詳細(xì)信息; ?} ??public function 查找($類型) ?{ ????return ($this->信息[$類型])();// 因?yàn)殚]包所以讓其執(zhí)行 ?} } $app = new 相親平臺(tái)(); ? $app->登記注入('御女和文雅' , function(){ ??return new 御女和文雅(); }); $app->登記注入('蘿莉和文雅' , function(){ ??return new 蘿莉和文雅(); }); ? $app->登記注入('單身同胞' , function(蘿莉和文雅 $r) use ($app){ ??return new 單身同胞($app->查找('蘿莉和文雅')); }); ? echo $app->查找('單身同胞')->f一起去玩(); 2.5 探索框架底層 IOC 與 DI 實(shí)現(xiàn) 因在第一章分析到框架的整體流程,IOC 和 DI 的相關(guān)特性已經(jīng)在源碼中,這里就帶大家看到重要的環(huán)節(jié)就可以了。從容器對(duì)象、解析對(duì)象、依賴注入對(duì)象。 class Container implements ArrayAccess, ContainerContract { ??/** ??* 全局可用容器,即把相關(guān)對(duì)象都注入到容器中 ??* @var static ??*/ ??protected static $instance; ??//注入框架自帶的核心類庫(kù)與對(duì)應(yīng)別名,想當(dāng)于是依賴注入。即容器注入應(yīng)用程序依賴的對(duì)象 ??public function registerCoreContainerAliases() ?{ stream_context_set_option(); foreach ([ ? 'app'?=> [ ????????self::class, ????????\Illuminate\Contracts\Container\Container::class, ????????\Illuminate\Contracts\Foundation\Application::class, ????????\Psr\Container\ContainerInterface::class ?????], ? 'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class], ? 'session'???=> [\Illuminate\Session\SessionManager::class], ? 'session.store'=> [\Illuminate\Session\Store::class, \Illuminate\Contracts\Session\Session::class], ? 'url'?=> [\Illuminate\Routing\UrlGenerator::class, \Illuminate\Contracts\Routing\UrlGenerator::class], ? 'validator'??=> [\Illuminate\Validation\Factory::class, \Illuminate\Contracts\Validation\Factory::class], ? 'view' => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class], ] as $key => $aliases) { ? foreach ($aliases as $alias) { $this->alias($key, $alias); ? } } ?} ??//綁定相關(guān)類庫(kù)到容器中,即注入相關(guān)外部資源類庫(kù),不單單是框架自帶的常用組件類。這里會(huì)綁定一個(gè)閉包函數(shù) ??public function bind($abstract, $concrete = null, $shared = false) ?{ $this->dropStaleInstances($abstract); ? if (is_null($concrete)) { ? $concrete = $abstract; } ? if (! $concrete instanceof Closure) { ? if (! is_string($concrete)) { throw new \TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null'); ? } ? ? $concrete = $this->getClosure($abstract, $concrete); } ? $this->bindings[$abstract] = compact('concrete', 'shared'); ? if ($this->resolved($abstract)) { ? $this->rebound($abstract); } ?} ?? ??//從容器中解析給定類型的資源,即獲取容器中的對(duì)象 ??public function make($abstract, array $parameters = []) ?{ return $this->resolve($abstract, $parameters); ?} ?? ??//先獲取注入到容器中的類,然后通過反射機(jī)制進(jìn)行類的實(shí)例化創(chuàng)建,最后返回對(duì)象到應(yīng)用 ??protected function resolve($abstract, $parameters = [], $raiseEvents = true) ?{ $abstract = $this->getAlias($abstract); ? $concrete = $this->getContextualConcrete($abstract); ? $needsContextualBuild = ! empty($parameters) || ! is_null($concrete); if (isset($this->instances[$abstract]) && ! $needsContextualBuild) { ? return $this->instances[$abstract]; } ? $this->with[] = $parameters; ? if (is_null($concrete)) { ? $concrete = $this->getConcrete($abstract); } ? ????if ($this->isBuildable($concrete, $abstract)) { ??????$object = $this->build($concrete); ???} else { ??????$object = $this->make($concrete); ???} ? ????foreach ($this->getExtenders($abstract) as $extender) { ??????$object = $extender($object, $this); ???} ? ????if ($this->isShared($abstract) && ! $needsContextualBuild) { ??????$this->instances[$abstract] = $object; ???} ? ????if ($raiseEvents) { ??????$this->fireResolvingCallbacks($abstract, $object); ???} ? ????$this->resolved[$abstract] = true; ? ????array_pop($this->with); ? ????return $object; ?} }