日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
教你如何手動(dòng)創(chuàng)建PHPDI容器

關(guān)于依賴注入相信大家應(yīng)該都經(jīng)常接觸或者至少有所耳聞,比較知名的框架都支持依賴注入,比如Java的Spring,php的Laravel、Symfony等?,F(xiàn)在我開始手動(dòng)實(shí)現(xiàn)一個(gè)簡陋的DI容器吧。

先開個(gè)車,為大家舉個(gè)栗子:

class Driver{    public function drive()
    {
        $car = new Car();        echo '老司機(jī)正在駕駛', $car->getCar(), PHP_EOL;
    }
}class Car{    protected $name = '普通汽車';    public function getCar()
    {        return $this->name;
    }
}

有兩個(gè)類,Driver和Car,老司機(jī)Driver有個(gè)方法driver,在調(diào)用的時(shí)候首先得整輛車$car,然后發(fā)車。大多數(shù)同學(xué)都寫過這樣或者類似的代碼,這樣的代碼單看沒啥毛病,挺正常的。但是,如果我要換輛車,開普通車撩不到妹。

class Benz extends Car{    protected $name = '奔馳';
}

這時(shí)候就需要做一個(gè)比較惡心的操作了,得改老司機(jī)的代碼了。(老司機(jī):我做錯(cuò)了什么?換輛車還得讓我重學(xué)駕照……)。因此我們需要把讓Car為外界注入,將Driver和Car解耦,不是老司機(jī)自己開車的時(shí)候還得自己去造車。于是就有了下面的結(jié)果

class Driver{    protected $car;    public function __construct(Car $car)
    {        $this->car = $car;
    }    public function drive()
    {        echo '老司機(jī)正在駕駛', $this->car->getCar(), PHP_EOL;
    }
}

此時(shí)Driver和Car兩個(gè)類已經(jīng)解耦,這兩個(gè)類的依賴,依靠上層代碼去管理。此時(shí),老司機(jī)會(huì)這樣“開車”:

$car = new Car();
$driver = new Driver($car);
$driver->drive();

此時(shí),我們創(chuàng)建Driver依賴的實(shí)例,并注入。上面的例子,我們實(shí)現(xiàn)了依賴注入,不過是手動(dòng)的,寫起來感覺還是不爽。這么繁重的活怎么能手動(dòng)來做呢,得讓程序自己去做。于是乎,DI容器誕生。

依賴注入容器

依賴注入與IoC模式類似工廠模式,是一種解決調(diào)用者和被調(diào)用者依賴耦合關(guān)系的模式。它解決了對象之間的依賴關(guān)系,使得對象只依賴IoC/DI容器,不再直接相互依賴,實(shí)現(xiàn)松耦合,然后在對象創(chuàng)建時(shí),由IoC/DI容器將其依賴(Dependency)的對象注入(Inject)其內(nèi),這樣做可以最大程度實(shí)現(xiàn)松耦合。依賴注入說白一點(diǎn),就是容器將某個(gè)類依賴的其他類的實(shí)例注入到這個(gè)類的實(shí)例中。

這段話可能說的有點(diǎn)抽象,回到剛才的例子吧。剛剛我手動(dòng)完成了依賴注入,比較麻煩,如果一個(gè)大型的項(xiàng)目這樣做肯定會(huì)覺得很繁瑣,而且不夠優(yōu)雅。因此我們需要有一位總管代替我們?nèi)ジ蛇@個(gè),這個(gè)總管就是容器。類的依賴管理全部交給容器去完成。因此,一般來說容器是一個(gè)全局的對象,大家共有的。

做一個(gè)自己的DI容器

寫一個(gè)功能,我們首先需要分析問題,因此我們先要明白,對于一個(gè)簡單的DI容器需要哪些功能,這直接關(guān)系到我們代碼的編寫。對于一個(gè)簡單的容器,至少需要滿足以下幾點(diǎn):

  • 創(chuàng)建所需類的實(shí)例

  • 完成依賴管理(DI)

  • 可以獲取單例的實(shí)例

  • 全局唯一

綜上,我們的容器類大約長這樣:

class Container{    /**
     * 單例
     * @var Container
     */
    protected static $instance;    /**
     * 容器所管理的實(shí)例
     * @var array
     */
    protected $instances = [];    private function __construct(){}  
    private function __clone(){}    /**
     * 獲取單例的實(shí)例
     * @param string $class
     * @param array ...$params
     * @return object
     */
    public function singleton($class, ...$params)
    {}    /**
     * 獲取實(shí)例(每次都會(huì)創(chuàng)建一個(gè)新的)
     * @param string $class
     * @param array ...$params
     * @return object
     */
    public function get($class, ...$params)
    {}    /**
     * 工廠方法,創(chuàng)建實(shí)例,并完成依賴注入
     * @param string $class
     * @param array $params
     * @return object
     */
    protected function make($class, $params = [])
    {}    /**
     * @return Container
     */
    public static function getInstance()
    {        if (null === static::$instance) {            static::$instance = new static();
        }        return static::$instance;
    }
}

大體骨架已經(jīng)確定,接下來進(jìn)入最核心的make方法:

protected function make($class, $params = []){  //如果不是反射類根據(jù)類名創(chuàng)建
  $class = is_string($class) ? new ReflectionClass($class) : $class;  //如果傳的入?yún)⒉粸榭?,則根據(jù)入?yún)?chuàng)建實(shí)例
  if (!empty($params)) {    return $class->newInstanceArgs($params);
  }  //獲取構(gòu)造方法
  $constructor = $class->getConstructor();  //獲取構(gòu)造方法參數(shù)
  $parameterClasses = $constructor ? $constructor->getParameters() : [];  if (empty($parameterClasses)) {    //如果構(gòu)造方法沒有入?yún)ⅲ苯觿?chuàng)建
    return $class->newInstance();
  } else {    //如果構(gòu)造方法有入?yún)?,迭代并遞歸創(chuàng)建依賴類實(shí)例
    foreach ($parameterClasses as $parameterClass) {
      $paramClass = $parameterClass->getClass();
      $params[] = $this->make($paramClass);
    }    //最后根據(jù)創(chuàng)建的參數(shù)創(chuàng)建實(shí)例,完成依賴的注入
    return $class->newInstanceArgs($params);
  }
}

為了容器的易用,我做了一些完善:

  • 實(shí)現(xiàn)ArrayAccess接口,使單例實(shí)例可以直接通過array的方式獲取,如果該實(shí)例沒有,則創(chuàng)建

  • 重寫__get方法,更方便的獲取

最終版:

class Container implements ArrayAccess{    /**
     * 單例
     * @var Container
     */
    protected static $instance;    /**
     * 容器所管理的實(shí)例
     * @var array
     */
    protected $instances = [];    private function __construct(){}    private function __clone(){}    /**
     * 獲取單例的實(shí)例
     * @param string $class
     * @param array  ...$params
     * @return object
     */
    public function singleton($class, ...$params)
    {        if (isset($this->instances[$class])) {            return $this->instances[$class];
        } else {            $this->instances[$class] = $this->make($class, $params);
        }        return $this->instances[$class];
    }    /**
     * 獲取實(shí)例(每次都會(huì)創(chuàng)建一個(gè)新的)
     * @param string $class
     * @param array  ...$params
     * @return object
     */
    public function get($class, ...$params)
    {        return $this->make($class, $params);
    }    /**
     * 工廠方法,創(chuàng)建實(shí)例,并完成依賴注入
     * @param string $class
     * @param array  $params
     * @return object
     */
    protected function make($class, $params = [])
    {        //如果不是反射類根據(jù)類名創(chuàng)建
        $class = is_string($class) ? new ReflectionClass($class) : $class;        //如果傳的入?yún)⒉粸榭?,則根據(jù)入?yún)?chuàng)建實(shí)例
        if (!empty($params)) {            return $class->newInstanceArgs($params);
        }        //獲取構(gòu)造方法
        $constructor = $class->getConstructor();        //獲取構(gòu)造方法參數(shù)
        $parameterClasses = $constructor ? $constructor->getParameters() : [];        if (empty($parameterClasses)) {            //如果構(gòu)造方法沒有入?yún)?,直接?chuàng)建
            return $class->newInstance();
        } else {            //如果構(gòu)造方法有入?yún)ⅲ⑦f歸創(chuàng)建依賴類實(shí)例
            foreach ($parameterClasses as $parameterClass) {
                $paramClass = $parameterClass->getClass();
                $params[] = $this->make($paramClass);
            }            //最后根據(jù)創(chuàng)建的參數(shù)創(chuàng)建實(shí)例,完成依賴的注入
            return $class->newInstanceArgs($params);
        }
    }    /**
     * @return Container
     */
    public static function getInstance()
    {        if (null === static::$instance) {            static::$instance = new static();
        }        return static::$instance;
    }    public function __get($class)
    {        if (!isset($this->instances[$class])) {            $this->instances[$class] = $this->make($class);
        }        return $this->instances[$class];
    }    public function offsetExists($offset)
    {        return isset($this->instances[$offset]);
    }    public function offsetGet($offset)
    {        if (!isset($this->instances[$offset])) {            $this->instances[$offset] = $this->make($offset);
        }        return $this->instances[$offset];
    }    public function offsetSet($offset, $value)
    {
    }    public function offsetUnset($offset) {        unset($this->instances[$offset]);
    }
}

現(xiàn)在借助容器我們寫一下上面的代碼:

$driver = $app->get(Driver::class);
$driver->drive();//output:老司機(jī)正在駕駛普通汽車復(fù)制代碼

就這么簡單,老司機(jī)就能發(fā)車。這里默認(rèn)注入的是Car的實(shí)例,如果需要開奔馳,那只需要這樣:

$benz = $app->get(Benz::class);
$driver = $app->get(Driver::class, $benz);
$driver->drive();//output:老司機(jī)正在駕駛奔馳復(fù)制代碼

按照PSR-11的要求,依賴注入容器需要實(shí)現(xiàn)Psr\Container\ContainerInterface接口,這里只是演示并未去實(shí)現(xiàn),因?yàn)槟切枰隤sr依賴庫,比較麻煩,其實(shí)也很簡單,只是多了幾個(gè)方法,有興趣的可以自己去了解下PSR-11的要求(傳送門)。

這里只是實(shí)現(xiàn)了一個(gè)非常簡陋的DI容器,實(shí)際中還需要考慮很多,而且這里的容器功能上還很簡陋。還有一些坑沒處理,比如出現(xiàn)循環(huán)依賴怎么處理、延遲加載的機(jī)制……


分享標(biāo)題:教你如何手動(dòng)創(chuàng)建PHPDI容器
URL分享:http://www.5511xx.com/article/ccdpohi.html