目錄
容器是什麼?
IOC是什麼?
依賴注入
依賴倒置
實戰:根據容器原理實作容器
實戰:實作依賴注入
首頁 後端開發 php教程 一文理解與實現現代PHP框架裡的IOC容器

一文理解與實現現代PHP框架裡的IOC容器

Dec 29, 2022 am 10:39 AM
php

這篇文章為大家帶來了關於PHP的相關知識,其中主要介紹了關於IOC容器的相關內容,IOC的全名是:Inversion Of Control,反轉控制,下面一起來看一下,希望對大家有幫助。

一文理解與實現現代PHP框架裡的IOC容器

容器是什麼?

相信很多人聽過依賴注入,依賴注入實作的基礎條件離不開容器,容器就是用來管理類別依賴和注入的,負責服務的管理和解耦元件,最簡單的理解我們可以把容器理解成一個超級大、專門存物件的陣列。

一文理解與實現現代PHP框架裡的IOC容器

如圖所示呼叫者透過容器的標示取得到物件實例,圖裡可以看出來,可以透過::class 的方式來取得也可以直接透過對象標示取得實例物件。

IOC是什麼?

大家可能都聽過IOC容器,IOC的全名是:(Inversion Of Control,反轉控制)。

我們來理解什麼是反轉控制,在我們傳統編碼中我們在類別與類別之間的依賴通常是我們透過編碼的方式new出來物件再傳遞的,而使用控制反轉我們可以把物件的控制權交給容器或是框架去實現。目的是為了讓我們不需要硬編碼去創建對象,看圖1可以知道,容器裡面存放著很多對象,當我們要使用的時候可以直接去用。而容器裡面的物件不需要我們在程式碼中編碼創建。在需要某個類別對象的時候會去容器裡面取得對象,如果對像不存在則會自動建立。這就是省略了我們在程式碼裡面去創建物件的過程,由容器去幫我們實現這個創建的過程,這就叫反轉控制。一句話總結IOC:把創建物件的控制權轉移給容器實作類別的實例化。

例如:沒有使用IOC的情況下,我們想要建立類別

<?php
class Sunny{
}
$sunny = new Sunny();
登入後複製

我們需要手動去new一個類,這種情況就是硬編碼在程式碼裡面去實現的。

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

<?php
class Sunny{
}
$sunny = Container::getBean(Sunny::class);
登入後複製

在容器的內部去幫我們實作這個類,有同學看到這裡可能會有疑問,我使用 new Sunny 不是程式碼寫得更短更簡單嗎?我們看完依賴注入再看一個例子。

依賴注入

現在知道了IOC是什麼,那麼一個新的問題出來了,我們在創建類別的時候有些類別的建構方法會需要我們傳遞參數怎麼辦?透過IOC的學習我們知道了IOC容器會幫我們解決這個物件實例創建的問題,那麼在容器裡面創建物件的時候發現類別有其他依賴則會進行依賴查找,容器尋找需要物件的過程,稱為DL( Dependency Lookup, 依賴查找)。而把需要的依賴注入到程式碼片段中這個稱為DI(Dependency Injection,依賴注入)。

例如IOC裡面說到的 new Sunny 這個例子。如果在類別與類別之間有多重依賴。

<?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 這個類別想要程式依賴類別 Computer 這個類,而如果使用IOC容器實作依賴注入的話,程式碼就簡單了。

<?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();
登入後複製

一句話總結:解決創建類別實例當中對其他類別的依賴,動態的向某個物件提供它所需要的其他物件。

依賴倒置

依賴倒置解決的問題是鬆耦各個模組之間的重度依賴,上層模組不應該依賴底層模組,它們都應該依賴抽象。通常簡單的理解依賴倒置就是面向介面或面向抽象來進行程式設計。我們透過下面的例子來看看面向介面程式設計。

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");
登入後複製

上面的這段程式碼看似沒有什麼大問題,但是如果有一天把檔案快取改成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");
登入後複製

透過這段程式碼可以看出來當一個快取使用的驅動改變了的時候,Cache的程式碼也必須做出對應的改變,因為程式碼寫死在呼叫者身上了,耦合度變得高了。再將程式碼改造一樣,讓程式設計師面向interface編程,讓程式碼變得更通用,更規範。

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");
登入後複製

很多人看到這段程式碼的時候想著,那我在建構方法直接把要的物件傳進去不就好了嗎?為什麼還要定義一個interface呢?其實定義interface是為了規範程式碼,不管你使用哪個驅動,只要實現了我這個interface的都可以用,沒有interface開發者在開發驅動的時候就會不知道這個驅動裡面該有什麼方法。當我們使用interface之後大家只要面向介面編程,Cache完全不管類別是怎麼實現的,Cache只是依照interface的方法進行操作。

一句話總結:依賴倒實現鬆散耦合

實戰:根據容器原理實作容器

<?php
class Container
{
    // 当前容器对象
    private static $instance;
    // 存放在容器里面到实例
    protected $instances = [];
    private function __construct()
{
    }
    public static function getInstance()
{
        if (!self::$instance) {
            self::$instance = new static();
        }
        return self::$instance;
    }
    /**
     * 获取对象实例
     * @param $key
     * @return mixed
     */
    public function get($key)
{
        if (isset($this->instances[$key])) {
            return $this->instances[$key];
        }
    }
    /**
     * 绑定对象、闭包、类到容器
     * @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();
登入後複製

實戰:實作依賴注入

Container.php
<?php
class Container
{
    // 当前容器对象
    private static $instance;
    // 存放在容器里面到实例
    protected $instances = [];
    private function __construct()
{
    }
    public static function getInstance()
{
        if (!self::$instance) {
            self::$instance = new static();
        }
        return self::$instance;
    }
    /**
     * 获取对象实例
     * @param $key
     * @return mixed
     * @throws ReflectionException
     */
    public function get($key)
{
        if (isset($this->instances[$key])) {
            return $this->instances[$key];
        }
        return $this->make($key);
    }
    /**
     * 绑定对象、闭包、类到容器
     * @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;
    }
    /**
     * 创建类绑定到类实例
     * @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);
        // 获取构造方法
        $construct = $reflectionClass->getConstructor();
        // 获取参数得到实例
        $params = $construct ? $this->parserParams($construct) : [];
        $object = $reflectionClass->newInstanceArgs($params);
        return $object;
    }
    /**
     * 解析构造方法参数
     * @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();
                // 默认值
                $def = null;
                // 如果有默认值,从默认值获取类型
                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();
登入後複製

推薦學習: 《PHP影片教學

以上是一文理解與實現現代PHP框架裡的IOC容器的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

適用於 Ubuntu 和 Debian 的 PHP 8.4 安裝和升級指南 適用於 Ubuntu 和 Debian 的 PHP 8.4 安裝和升級指南 Dec 24, 2024 pm 04:42 PM

PHP 8.4 帶來了多項新功能、安全性改進和效能改進,同時棄用和刪除了大量功能。 本指南介紹如何在 Ubuntu、Debian 或其衍生版本上安裝 PHP 8.4 或升級到 PHP 8.4

我後悔之前不知道的 7 個 PHP 函數 我後悔之前不知道的 7 個 PHP 函數 Nov 13, 2024 am 09:42 AM

如果您是經驗豐富的PHP 開發人員,您可能會感覺您已經在那裡並且已經完成了。操作

如何設定 Visual Studio Code (VS Code) 進行 PHP 開發 如何設定 Visual Studio Code (VS Code) 進行 PHP 開發 Dec 20, 2024 am 11:31 AM

Visual Studio Code,也稱為 VS Code,是一個免費的原始碼編輯器 - 或整合開發環境 (IDE) - 可用於所有主要作業系統。 VS Code 擁有大量針對多種程式語言的擴展,可以輕鬆編寫

在PHP API中說明JSON Web令牌(JWT)及其用例。 在PHP API中說明JSON Web令牌(JWT)及其用例。 Apr 05, 2025 am 12:04 AM

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

php程序在字符串中計數元音 php程序在字符串中計數元音 Feb 07, 2025 pm 12:12 PM

字符串是由字符組成的序列,包括字母、數字和符號。本教程將學習如何使用不同的方法在PHP中計算給定字符串中元音的數量。英語中的元音是a、e、i、o、u,它們可以是大寫或小寫。 什麼是元音? 元音是代表特定語音的字母字符。英語中共有五個元音,包括大寫和小寫: a, e, i, o, u 示例 1 輸入:字符串 = "Tutorialspoint" 輸出:6 解釋 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。總共有 6 個元

您如何在PHP中解析和處理HTML/XML? 您如何在PHP中解析和處理HTML/XML? Feb 07, 2025 am 11:57 AM

本教程演示瞭如何使用PHP有效地處理XML文檔。 XML(可擴展的標記語言)是一種用於人類可讀性和機器解析的多功能文本標記語言。它通常用於數據存儲

解釋PHP中的晚期靜態綁定(靜態::)。 解釋PHP中的晚期靜態綁定(靜態::)。 Apr 03, 2025 am 12:04 AM

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

什麼是PHP魔術方法(__ -construct,__destruct,__call,__get,__ set等)並提供用例? 什麼是PHP魔術方法(__ -construct,__destruct,__call,__get,__ set等)並提供用例? Apr 03, 2025 am 12:03 AM

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。

See all articles