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

資訊專欄INFORMATION COLUMN

PHP IOC/DI 容器 - 依賴自動(dòng)注入/依賴單例注入/依賴契約注入/參數(shù)關(guān)聯(lián)傳值

Paul_King / 3118人閱讀

摘要:標(biāo)量參數(shù)關(guān)聯(lián)傳值依賴是自動(dòng)解析注入的,剩余的標(biāo)量參數(shù)則可以通過(guò)關(guān)聯(lián)傳值,這樣比較靈活,沒(méi)必要把默認(rèn)值的參數(shù)放在函數(shù)參數(shù)最尾部。

更新:github(給個(gè)小星星呀

-- 2018-4-11:優(yōu)化服務(wù)綁定方法 ::bind 的類型檢查模式

借助 PHP 反射機(jī)制實(shí)現(xiàn)的一套 依賴自動(dòng)解析注入 的 IOC/DI 容器,可以作為 Web MVC 框架 的應(yīng)用容器

1、依賴的自動(dòng)注入:你只需要在需要的位置注入你需要的依賴即可,運(yùn)行時(shí)容器會(huì)自動(dòng)解析依賴(存在子依賴也可以自動(dòng)解析)將對(duì)應(yīng)的實(shí)例注入到你需要的位置。

2、依賴的單例注入:某些情況下我們需要保持依賴的全局單例特性,比如 Web 框架中的 Request 依賴,我們需要將整個(gè)請(qǐng)求響應(yīng)周期中的所有注入 Request 依賴的位置同步為在路由階段解析完請(qǐng)求體的 Request 實(shí)例,這樣我們?cè)谌魏挝恢枚伎梢栽L問(wèn)全局的請(qǐng)求體對(duì)象。

3、依賴的契約注入:比如我們依賴某 Storage,目前使用 FileStorage 來(lái)實(shí)現(xiàn),后期發(fā)現(xiàn)性能瓶頸,要改用 RedisStorage 來(lái)實(shí)現(xiàn),如果代碼中大量使用 FileStorage 作為依賴注入,這時(shí)候就需要花費(fèi)精力去改代碼了。我們可以使用接口 Storage 作為契約,將具體的實(shí)現(xiàn)類 FileStorage / RedisStorage 通過(guò)容器的綁定機(jī)制關(guān)聯(lián)到 Storage 上,依賴注入 Storage,后期切換存儲(chǔ)引擎只需要修改綁定即可。

4、標(biāo)量參數(shù)關(guān)聯(lián)傳值:依賴是自動(dòng)解析注入的,剩余的標(biāo)量參數(shù)則可以通過(guò)關(guān)聯(lián)傳值,這樣比較靈活,沒(méi)必要把默認(rèn)值的參數(shù)放在函數(shù)參數(shù)最尾部。這點(diǎn)我還是蠻喜歡 python 的函數(shù)傳值風(fēng)格的。

function foo($name, $age = 27, $sex)
{
    // php 沒(méi)辦法 foo($name = "big cat", $sex = "male") 這樣傳值
    // 只能 foo("big cat", 27, "male") 傳值...
    // python 可以 foo(name = "big cat", sex = "male") 很舒服
}

但這也使得我的容器不支持位序傳值,必須保證運(yùn)行參數(shù)的鍵名與運(yùn)行方法的參數(shù)名準(zhǔn)確的關(guān)聯(lián)映(有默認(rèn)值的參數(shù)可以省略),我想著并沒(méi)有什么不方便的地方吧,我不喜歡給 $bar 參數(shù)傳遞個(gè) $foo 變量。

容器源碼
 $provider,
            "singleton" => $singleton,
        ];
    }

    /**
     * 獲取類實(shí)例
     * 通過(guò)反射獲取構(gòu)造參數(shù)
     * 返回對(duì)應(yīng)的類實(shí)例
     * @param  [type] $class_name [description]
     * @return [type]             [description]
     */
    private static function getInstance($class_name)
    {
        //方法參數(shù)分為 params 和 default_values
        //如果一個(gè)開(kāi)放構(gòu)造類作為依賴注入傳入它類,我們應(yīng)該將此類注冊(cè)為全局單例服務(wù)
        $params = static::getParams($class_name);
        return (new ReflectionClass($class_name))->newInstanceArgs($params["params"]);
    }

    /**
     * 反射方法參數(shù)類型
     * 對(duì)象參數(shù):構(gòu)造對(duì)應(yīng)的實(shí)例 同時(shí)檢查是否為單例模式的實(shí)例
     * 標(biāo)量參數(shù):返回參數(shù)名 索引路由參數(shù)取值
     * 默認(rèn)值參數(shù):檢查路由參數(shù)中是否存在本參數(shù) 無(wú)則取默認(rèn)值
     * @param  [type] $class_name [description]
     * @param  string $method     [description]
     * @return [type]             [description]
     */
    private static function getParams($class_name, $method = "__construct")
    {
        $params_set["params"] = array();
        $params_set["default_values"] = array();

        //反射檢測(cè)類是否顯示聲明或繼承父類的構(gòu)造方法
        //若無(wú)則說(shuō)明構(gòu)造參數(shù)為空
        if ($method == "__construct") {
            $classRf = new ReflectionClass($class_name);
            if (! $classRf->hasMethod("__construct")) {
                return $params_set;
            }
        }

        //反射方法 獲取參數(shù)
        $methodRf = new ReflectionMethod($class_name, $method);
        $params = $methodRf->getParameters();

        if (! empty($params)) {
            foreach ($params as $key => $param) {
                if ($paramClass = $param->getClass()) {// 對(duì)象參數(shù) 獲取對(duì)象實(shí)例
                    $param_class_name = $paramClass->getName();
                    if (array_key_exists($param_class_name, static::$dependencyServices)) {// 是否為注冊(cè)的服務(wù)
                        if (static::$dependencyServices[$param_class_name]["singleton"]) {// 單例模式直接返回已注冊(cè)的實(shí)例
                            $params_set["params"][] = static::$dependencyServices[$param_class_name]["provider"];
                        } else {// 非單例則返回提供者的新的實(shí)例
                            $params_set["params"][] = static::getInstance(static::$dependencyServices[$param_class_name]["provider"]);
                        }
                    } else {// 沒(méi)有做綁定注冊(cè)的類
                        $params_set["params"][] = static::getInstance($param_class_name);
                    }
                } else {// 標(biāo)量參數(shù) 獲取變量名作為路由映射 包含默認(rèn)值的記錄默認(rèn)值
                    $param_name = $param->getName();

                    if ($param->isDefaultValueAvailable()) {// 是否包含默認(rèn)值
                        $param_default_value = $param->getDefaultValue();
                        $params_set["default_values"][$param_name] = $param_default_value;
                    }

                    $params_set["params"][] = $param_name;
                }
            }
        }

        return $params_set;
    }

    /**
     * 容器的運(yùn)行入口 主要負(fù)責(zé)加載類方法,并將運(yùn)行所需的標(biāo)量參數(shù)做映射和默認(rèn)值處理
     * @param  [type] $class_name 運(yùn)行類
     * @param  [type] $method     運(yùn)行方法
     * @param  array  $params     運(yùn)行參數(shù)
     * @return [type]             輸出
     */
    public static function run($class_name, $method, array $params = array())
    {
        if (! class_exists($class_name)) {
            throw new Exception($class_name . "not found!", 4040);
        }

        if (! method_exists($class_name, $method)) {
            throw new Exception($class_name . "::" . $method . " not found!", 4041);
        }

        // 獲取要運(yùn)行的類
        $classInstance = static::getInstance($class_name);
        // 獲取要運(yùn)行的方法的參數(shù)
        $method_params = static::getParams($class_name, $method);
        
        // 關(guān)聯(lián)傳入的運(yùn)行參數(shù)
        $method_params = array_map(function ($param) use ($params, $method_params) {
            if (is_object($param)) {// 對(duì)象參數(shù) 以完成依賴解析的具體實(shí)例
                return $param;
            }

            // 以下為關(guān)聯(lián)傳值 可通過(guò)參數(shù)名映射的方式關(guān)聯(lián)傳值 可省略含有默認(rèn)值的參數(shù)
            if (array_key_exists($param, $params)) {// 映射傳遞路由參數(shù)
                return $params[$param];
            }

            if (array_key_exists($param, $method_params["default_values"])) {// 默認(rèn)值
                return $method_params["default_values"][$param];
            }

            throw new Exception($param . " is necessary parameters", 4042); // 路由中沒(méi)有的則包含默認(rèn)值
        }, $method_params["params"]);

        // 運(yùn)行
        return call_user_func_array([$classInstance, $method], $method_params);
    }
}
演示所需的依賴類
// 它將被以單例模式注入 全局的所有注入點(diǎn)都使用的同一實(shí)例
class Foo
{
    public $msg = "foo nothing to say!";

    public function index()
    {
        $this->msg = "foo hello, modified by index method!";
    }
}

// 它將以普通依賴模式注入 各注入點(diǎn)會(huì)分別獲取一個(gè)實(shí)例
class Bar
{
    public $msg = "bar nothing to say!";

    public function index()
    {
        $this->msg = "bar hello, modified by index method!";
    }
}

// 契約注入
interface StorageEngine
{
    public function info();
}

// 契約實(shí)現(xiàn)
class FileStorageEngine implements StorageEngine
{
    public $msg = "file storage engine!" . PHP_EOL;

    public function info()
    {
        $this->msg =  "file storage engine!" . PHP_EOL;
    }
}

// 契約實(shí)現(xiàn)
class RedisStorageEngine implements StorageEngine
{
    public $msg = "redis storage engine!" . PHP_EOL;

    public function info()
    {
        $this->msg =  "redis storage engine!" . PHP_EOL;
    }
}
演示所需的運(yùn)行類
// 具體的運(yùn)行類
class BigCatController
{
    public $foo;
    public $bar;

    // 這里自動(dòng)注入一次 Foo 和 Bar 的實(shí)例
    public function __construct(Foo $foo, Bar $bar)
    {
        $this->foo = $foo;
        $this->bar = $bar;
    }

    // 這里的參數(shù)你完全可以亂序的定義(我故意寫的很亂序),你只需保證 route 參數(shù)中存在對(duì)應(yīng)的必要參數(shù)即可
    // 默認(rèn)值參數(shù)可以直接省略
    public function index($name = "big cat", Foo $foo, $sex = "male", $age, Bar $bar, StorageEngine $se)
    {
        // Foo 為單例模式注入 $this->foo $foo 是同一實(shí)例
        $this->foo->index();
        echo $this->foo->msg . PHP_EOL;
        echo $foo->msg . PHP_EOL;
        echo "------------------------------" . PHP_EOL;

        // Bar 為普通模式注入 $this->bar $bar 為兩個(gè)不同的 Bar 的實(shí)例
        $this->bar->index();
        echo $this->bar->msg . PHP_EOL;
        echo $bar->msg . PHP_EOL;
        echo "------------------------------" . PHP_EOL;

        // 契約注入 具體看你為契約者綁定了哪個(gè)具體的實(shí)現(xiàn)類
        // 我們綁定的 RedisStorageEngine 所以這里注入的是 RedisStorageEngine 的實(shí)例
        $se->info();
        echo $se->msg;
        echo "------------------------------" . PHP_EOL;

        // 返回個(gè)值
        return "name " . $name . ", age " . $age . ", sex " . $sex . PHP_EOL;
    }
}
運(yùn)行
// 路由信息很 MVC 吧
$route = [
    "controller" => BigCatController::class, // 運(yùn)行的類
    "action"     => "index", // 運(yùn)行的方法
    "params"     => [ // 運(yùn)行的參數(shù)
        "name" => "big cat",
        "age"  => 27 // sex 有默認(rèn)值 不傳
    ]
];

try {
    // 依賴的單例注冊(cè)
    IOCContainer::singleton(Foo::class, new Foo());

    // 依賴的契約注冊(cè) StorageEngine 相當(dāng)于契約者 注冊(cè)關(guān)聯(lián)具體的實(shí)現(xiàn)類
    // IOCContainer::bind(StorageEngine::class, FileStorageEngine::class);
    IOCContainer::bind(StorageEngine::class, RedisStorageEngine::class);
    
    // 運(yùn)行
    $result = IOCContainer::run($route["controller"], $route["action"], $route["params"]);
    
    echo $result;
} catch (Exception $e) {
    echo $e->getMessage();
}
運(yùn)行結(jié)果
foo hello, modified by index method!
foo hello, modified by index method!
------------------------------
bar hello, modified by index method!
bar nothing to say!
------------------------------
redis storage engine!
------------------------------
name big cat, age 27, sex male

簡(jiǎn)單的實(shí)現(xiàn)了像 laraval 的 IOC 容器的特性,但比它多一項(xiàng)(可能也比較雞肋)標(biāo)量參數(shù)的關(guān)聯(lián)傳值,不過(guò)我這功能也限定死了你傳入的參數(shù)必須與函數(shù)定義的參數(shù)名相關(guān)聯(lián),可我還是覺(jué)得能充分的填補(bǔ)默認(rèn)參數(shù)不放在參數(shù)尾就無(wú)法跳過(guò)的強(qiáng)迫癥問(wèn)題.....

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/23216.html

相關(guān)文章

  • PHP程序員如何理解依賴注入容器(dependency injection container)

    摘要:代碼這就是控制反轉(zhuǎn)模式。是變量有默認(rèn)值則設(shè)置默認(rèn)值是一個(gè)類,遞歸解析有默認(rèn)值則返回默認(rèn)值從容器中取得以上代碼的原理參考官方文檔反射,具有完整的反射,添加了對(duì)類接口函數(shù)方法和擴(kuò)展進(jìn)行反向工程的能力。 PHP程序員如何理解依賴注入容器(dependency injection container) 背景知識(shí) 傳統(tǒng)的思路是應(yīng)用程序用到一個(gè)Foo類,就會(huì)創(chuàng)建Foo類并調(diào)用Foo類的方法,假如這...

    Coding01 評(píng)論0 收藏0
  • 聊一聊PHP依賴注入(DI) 和 控制反轉(zhuǎn)(IoC)

    摘要:前言最近在使用框架,看了下他的源碼,發(fā)現(xiàn)有很多地方也用到了依賴注入控制反轉(zhuǎn),覺(jué)得有必要和大家簡(jiǎn)單聊一聊什么是依賴注入以及怎么使用它。概念依賴注入和控制反轉(zhuǎn)是對(duì)同一件事情的不同描述,從某個(gè)方面講,就是它們描述的角度不同。 前言 最近在使用ThinkPHP5框架,看了下他的源碼,發(fā)現(xiàn)有很多地方也用到了依賴注入(控制反轉(zhuǎn)),覺(jué)得有必要和大家簡(jiǎn)單聊一聊什么是依賴注入以及怎么使用它。 簡(jiǎn)介 I...

    sixgo 評(píng)論0 收藏0
  • PHP程序員如何理解IoC/DI

    摘要:依賴注入容器管理應(yīng)用程序中的全局對(duì)象包括實(shí)例化處理依賴關(guān)系。為了解決這樣的問(wèn)題,我們?cè)俅位氐饺肿?cè)表創(chuàng)建組件。參考文章程序員如何理解依賴注入容器補(bǔ)充很多代碼背后,都是某種哲學(xué)思想的體現(xiàn)。 思想 思想是解決問(wèn)題的根本思想必須轉(zhuǎn)換成習(xí)慣構(gòu)建一套完整的思想體系是開(kāi)發(fā)能力成熟的標(biāo)志——《簡(jiǎn)單之美》(前言) . 成功的軟件項(xiàng)目就是那些提交產(chǎn)物達(dá)到或超出客戶的預(yù)期的項(xiàng)目,而且開(kāi)發(fā)過(guò)程符合時(shí)間和費(fèi)...

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

    摘要:簡(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è)開(kāi)源框架,Spring是于2003 年興起的一個(gè)輕量級(jí)的Java 開(kāi)發(fā)框架,由Rod Johnson 在其著作Expert...

    reclay 評(píng)論0 收藏0
  • DIP、IoCDI、JS

    摘要:維基百科該原則規(guī)定高層次的模塊不應(yīng)該依賴與低層次的模塊,兩者都應(yīng)該依賴于抽象接口。依賴反轉(zhuǎn)原則則顛倒這種依賴關(guān)系,并以上面提到的兩個(gè)規(guī)定作為指導(dǎo)思想。維基百科這些話的意思就是將依賴對(duì)象的創(chuàng)建和綁定轉(zhuǎn)移到被依賴對(duì)象類的外部來(lái)實(shí)現(xiàn)。 在這個(gè)標(biāo)題中,除了 JS 是亂入之外,其它的幾個(gè)詞匯都是存在一個(gè)共同點(diǎn)的,那就是依賴。 那么,依賴是什么呢? 比如,現(xiàn)在我正在寫這篇博客文,但是我得在電腦上編...

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

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

0條評(píng)論

Paul_King

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<