溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

現(xiàn)代PHP框架里的IOC容器怎么實(shí)現(xiàn)

發(fā)布時(shí)間:2022-12-29 11:17:07 來(lái)源:億速云 閱讀:129 作者:iii 欄目:編程語(yǔ)言

這篇文章主要講解了“現(xiàn)代PHP框架里的IOC容器怎么實(shí)現(xiàn)”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“現(xiàn)代PHP框架里的IOC容器怎么實(shí)現(xiàn)”吧!

容器是什么?

相信很多人聽說(shuō)過(guò)依賴注入,依賴注入實(shí)現(xiàn)的基礎(chǔ)條件離不開容器,容器就是用來(lái)管理類依賴和注入的,負(fù)責(zé)服務(wù)的管理和解耦組件,最簡(jiǎn)單的理解我們可以把容器理解成一個(gè)超級(jí)大、專門存對(duì)象的數(shù)組。

現(xiàn)代PHP框架里的IOC容器怎么實(shí)現(xiàn)

如圖所示調(diào)用者通過(guò)容器的標(biāo)示獲取到對(duì)象實(shí)例,圖里可以看出來(lái),可以通過(guò) ::class 的方式來(lái)獲取也可以直接通過(guò)對(duì)象標(biāo)示獲取實(shí)例對(duì)象。

IOC是什么?

大家可能都聽說(shuō)過(guò)IOC容器,IOC的全稱是:(Inversion Of Control,反轉(zhuǎn)控制)。

我們來(lái)理解一下什么是反轉(zhuǎn)控制,在我們傳統(tǒng)編碼中我們?cè)陬惻c類之間的依賴通常是我們通過(guò)編碼的方式new出來(lái)對(duì)象再傳遞的,而使用控制反轉(zhuǎn)我們可以把對(duì)象的控制權(quán)交給容器或者框架去實(shí)現(xiàn)。目的是為了讓我們不需要硬編碼去創(chuàng)建對(duì)象,看圖1可以知道,容器里面存放著很多對(duì)象,當(dāng)我們要使用的時(shí)候可以直接去用。而容器里面的對(duì)象不需要我們?cè)诖a中編碼創(chuàng)建。在需要某個(gè)類對(duì)象的時(shí)候會(huì)去容器里面獲取對(duì)象,如果對(duì)象不存在則會(huì)自動(dòng)創(chuàng)建。這就是省略了我們?cè)诖a里面去創(chuàng)建對(duì)象的過(guò)程,由容器去幫我們實(shí)現(xiàn)這個(gè)創(chuàng)建的過(guò)程,這就叫反轉(zhuǎn)控制。一句話總結(jié)IOC:把創(chuàng)建對(duì)象的控制權(quán)轉(zhuǎn)移給容器實(shí)現(xiàn)類的實(shí)例化。

例如:沒(méi)有使用IOC的情況下,我們想要?jiǎng)?chuàng)建類

<?php
class Sunny{
}
$sunny = new Sunny();

我們需要手動(dòng)去new一個(gè)類,這種情況就是硬編碼在代碼里面去實(shí)現(xiàn)的。

而使用IOC容器的代碼則可以這樣寫。

<?php
class Sunny{
}
$sunny = Container::getBean(Sunny::class);

在容器的內(nèi)部去幫我們實(shí)現(xiàn)這個(gè)類,有同學(xué)看到這里可能會(huì)有疑問(wèn),我使用 new Sunny 不是代碼寫得更短更簡(jiǎn)單嗎?我們看完依賴注入再看一個(gè)例子。

依賴注入

現(xiàn)在知道了IOC是什么,那么一個(gè)新的問(wèn)題出來(lái)了,我們?cè)趧?chuàng)建類的時(shí)候有些類的構(gòu)造方法會(huì)需要我們傳遞參數(shù)怎么辦?通過(guò)IOC的學(xué)習(xí)我們知道了IOC容器會(huì)幫我們解決這個(gè)對(duì)象實(shí)例創(chuàng)建的問(wèn)題,那么在容器里面創(chuàng)建對(duì)象的時(shí)候發(fā)現(xiàn)類有其他依賴則會(huì)進(jìn)行依賴查找,容器尋找需要對(duì)象的過(guò)程,稱為DL(Dependency Lookup, 依賴查找)。而把需要的依賴注入到代碼片段中這個(gè)稱為DI(Dependency Injection,依賴注入)。

例如IOC里面說(shuō)到的 new Sunny 這個(gè)例子。如果在類與類之間有多重依賴。

<?php
class Computer{
    public function run(){
        echo "編程中....\n";
    }
}
class Sunny{
    private $computer;
    public function __construct(Computer $computer){
        $this->computer = $computer;
    }
    public function program(){
        $this->computer->run();
    }
}
$sunny = new Sunny(new Computer());
$sunny->program();

這里可以看到 Sunny 這個(gè)類想要編程依賴類 Computer 這個(gè)類,而如果使用IOC容器實(shí)現(xiàn)依賴注入的話,代碼就簡(jiǎn)單了。

<?php
class Computer{
    public function run(){
        echo "編程中....\n";
    }
}
class Sunny{
    private $computer;
    public function __construct(Computer $computer){
        $this->computer = $computer;
    }
    public function program(){
        $this->computer->run();
    }
}
$sunny = Container::getBean(Sunny::class);
$sunny->program();

一句話總結(jié):解決創(chuàng)建類實(shí)例當(dāng)中對(duì)其他類的依賴,動(dòng)態(tài)的向某個(gè)對(duì)象提供它所需要的其他對(duì)象。

依賴倒置

依賴倒置解決的問(wèn)題是松耦各個(gè)模塊之間的重度依賴,上層模塊不應(yīng)該依賴底層模塊,它們都應(yīng)該依賴于抽象。通常簡(jiǎn)單的理解依賴倒置就是面向接口或者面向抽象來(lái)進(jìn)行編程。我們通過(guò)下面的例子來(lái)看看面向接口編程。

class Cache{
    public function set($key,$value){
        $redis = new CFile();
        $redis->set($key,$value);
    }
}
class CFile{
    public function set($key,$value){
        echo "file:{$key}->{$value}\n";
    }
}
$cache = new Cache();
$cache->set("name","sunny");

上面的這段代碼看似沒(méi)有什么大問(wèn)題,但是如果有一天把文件緩存改成Redis緩存呢?

class Cache{
    public function set($key,$value){
        $redis = new CRedis();
        $redis->set($key,$value);
    }
}
class CRedis{
    public function set($key,$value){
        echo "redis:{$key}->{$value}\n";
    }
}
$cache = new Cache();
$cache->set("name","sunny");

通過(guò)這段代碼可以看出來(lái)當(dāng)一個(gè)緩存使用的驅(qū)動(dòng)改變了的時(shí)候,Cache的代碼也必須作出相應(yīng)的改變,因?yàn)榇a寫死在調(diào)用者身上了,耦合度變得高了。再對(duì)代碼進(jìn)行改造一樣,讓程序員面向interface編程,讓代碼變得更通用,更規(guī)范。

interface ICache{
    public function set($key,$value);
}
class CRedis implements ICache {
    public function set($key,$value)
{
        echo "redis:{$key}->{$value}\n";
    }
}
class CFile implements ICache{
    public function set($key,$value)
{
        echo "file:{$key}->{$value}\n";
    }
}
class Cache{
    private $drive;
    public function __construct(ICache $drive)
{
        $this->drive = $drive;
    }
    public function set($key,$value){
        $this->drive->set($key,$value);
    }
}
$cache = new Cache(new CFile());
$cache->set("name","sunny");

很多人看到這段代碼的時(shí)候想著,那我在構(gòu)造方法直接把要的對(duì)象傳進(jìn)去不就好了嗎?為什么還要定義一個(gè)interface呢?其實(shí)定義interface是為了規(guī)范代碼,不管你使用哪個(gè)驅(qū)動(dòng),只要實(shí)現(xiàn)了我這個(gè)interface的都可以用,沒(méi)有interface開發(fā)者在開發(fā)驅(qū)動(dòng)的時(shí)候就會(huì)不知道這個(gè)驅(qū)動(dòng)里面該有什么方法。當(dāng)我們使用interface之后大家只要面向接口編程,Cache完全不管類是怎么實(shí)現(xiàn)的,Cache只是根據(jù)interface的方法進(jìn)行操作。

一句話總結(jié):依賴倒置實(shí)現(xiàn)松耦合

實(shí)戰(zhàn):根據(jù)容器原理實(shí)現(xiàn)容器

<?php
class Container
{
    // 當(dāng)前容器對(duì)象
    private static $instance;
    // 存放在容器里面到實(shí)例
    protected $instances = [];
    private function __construct()
{
    }
    public static function getInstance()
{
        if (!self::$instance) {
            self::$instance = new static();
        }
        return self::$instance;
    }
    /**
     * 獲取對(duì)象實(shí)例
     * @param $key
     * @return mixed
     */
    public function get($key)
{
        if (isset($this->instances[$key])) {
            return $this->instances[$key];
        }
    }
    /**
     * 綁定對(duì)象、閉包、類到容器
     * @param $key
     * @param null $concrete
     * @return Container
     */
    public function bind($key, $concrete = null)
{
        if ($concrete instanceof Closure) {
            $this->instances[$key] = $concrete;
        } elseif (is_object($concrete)) {
            $this->instances[$key] = $concrete;
        }
        return $this;
    }
}
class Sunny
{
    public function getName()
{
        echo time() . "\n";
    }
}
$app = Container::getInstance();
$sunny = $app->bind(Sunny::class,new Sunny());
$sunny = $app->get(Sunny::class);
$sunny->getName();

實(shí)戰(zhàn):實(shí)現(xiàn)依賴注入

Container.php
<?php
class Container
{
    // 當(dāng)前容器對(duì)象
    private static $instance;
    // 存放在容器里面到實(shí)例
    protected $instances = [];
    private function __construct()
{
    }
    public static function getInstance()
{
        if (!self::$instance) {
            self::$instance = new static();
        }
        return self::$instance;
    }
    /**
     * 獲取對(duì)象實(shí)例
     * @param $key
     * @return mixed
     * @throws ReflectionException
     */
    public function get($key)
{
        if (isset($this->instances[$key])) {
            return $this->instances[$key];
        }
        return $this->make($key);
    }
    /**
     * 綁定對(duì)象、閉包、類到容器
     * @param $key
     * @param null $concrete
     * @return Container
     * @throws ReflectionException
     */
    public function bind($key, $concrete = null)
{
        if ($concrete instanceof Closure) {
            $this->instances[$key] = $concrete;
        } elseif (is_object($concrete)) {
            $this->instances[$key] = $concrete;
        } else {
            $this->make($key, $concrete);
        }
        return $this;
    }
    /**
     * 創(chuàng)建類綁定到類實(shí)例
     * @param $abstract
     * @param null $atgs
     * @return mixed
     * @throws ReflectionException
     */
    public function make($abstract, $atgs = null)
{
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }
        $object = $this->invokeClass($abstract);
        $this->instances[$abstract] = $object;
        return $object;
    }
    /**
     * 反射解析類
     * @param $abstract
     * @return object
     * @throws ReflectionException
     */
    public function invokeClass($abstract)
{
        $reflectionClass = new \ReflectionClass($abstract);
        // 獲取構(gòu)造方法
        $construct = $reflectionClass->getConstructor();
        // 獲取參數(shù)得到實(shí)例
        $params = $construct ? $this->parserParams($construct) : [];
        $object = $reflectionClass->newInstanceArgs($params);
        return $object;
    }
    /**
     * 解析構(gòu)造方法參數(shù)
     * @param $reflect
     * @return array
     * @throws ReflectionException
     */
    public function parserParams(ReflectionMethod $reflect)
{
        $args = [];
        $params = $reflect->getParameters();
        if (!$params) {
            return $args;
        }
        if (count($params) > 0) {
            foreach ($params as $param) {
                $class = $param->getClass();
                if ($class) {
                    $args[] = $this->make($class->getName());
                    continue;
                }
                // 獲取變量的名稱
                $name = $param->getName();
                // 默認(rèn)值
                $def = null;
                // 如果有默認(rèn)值,從默認(rèn)值獲取類型
                if ($param->isOptional()) {
                    $def = $param->getDefaultValue();
                }
                $args[] = $_REQUEST[$name] ?? $def;
            }
        }
        return $args;
    }
}
Test.php
<?php
class Test
{
    public $name;
    private $test1;
    public function __construct(Test1 $test1)
{
        $this->test1 = $test1;
        $this->name = $this->test1->getName();
    }
}
Test1.php
<?php
class Test1
{
    public function getName(){
        return "test1返回的名字";
    }
}
Sunny.php
<?php
require_once "./Container.php";
require_once "./Test.php";
require_once "./Test1.php";
class Sunny
{
    private $test;
    public function __construct(Test $test)
{
        $this->test = $test;
    }
    public function getName()
{
        echo "獲取test里面的name:{$this->test->name}\n";
    }
}
$app = Container::getInstance();
$sunny = $app->get(Sunny::class);
$sunny->getName();

感謝各位的閱讀,以上就是“現(xiàn)代PHP框架里的IOC容器怎么實(shí)現(xiàn)”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)現(xiàn)代PHP框架里的IOC容器怎么實(shí)現(xiàn)這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI