成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從yii2框架中的di容器源碼中了解反射的作用

dantezhao / 802人閱讀

摘要:反射簡(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方法吧。

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
處理變量。

getDependencies方法調(diào)用
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

相關(guān)文章

  • Yii2依賴注入

    摘要:構(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)用在低層次的模塊上)形式,使得高層次的模塊不依賴于...

    harriszh 評(píng)論0 收藏0
  • Yii源碼解讀-依賴注入(容器

    摘要:在中使用解耦,有兩種注入方式構(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è)接口,從而使的下...

    Prasanta 評(píng)論0 收藏0
  • Spring筆記01_下載_概述_監(jiān)聽器

    摘要:簡(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...

    reclay 評(píng)論0 收藏0
  • Java深入-框架技巧

    摘要:從使用到原理學(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)題列表 -...

    chengtao1633 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<