ホームページ >バックエンド開発 >PHPチュートリアル >最新の PHP フレームワークで IOC コンテナを理解して実装するための 1 つの記事

最新の PHP フレームワークで IOC コンテナを理解して実装するための 1 つの記事

WBOY
WBOY転載
2022-12-29 10:39:074801ブラウズ

この記事では、PHP に関する関連知識をお届けします。主に IOC コンテナに関する関連コンテンツを紹介します。IOC の正式名称は、Inversion Of Control、反転制御です。一緒に話しましょう。見てください、それが皆さんのお役に立てば幸いです。

最新の PHP フレームワークで IOC コンテナを理解して実装するための 1 つの記事

#コンテナとは何ですか?

依存関係注入について聞いたことがある人は多いと思います。依存関係注入の実装の基本条件はコンテナと切り離せません。コンテナはクラスの依存関係と注入を管理するために使用され、サービス管理とコンポーネントの分離を担当します。最も単純なものは、コンテナとは、オブジェクトを格納するための専用の超大規模な配列として理解できます。

最新の PHP フレームワークで IOC コンテナを理解して実装するための 1 つの記事

図に示すように、呼び出し元はコンテナのラベルを通じてオブジェクト インスタンスを取得します。図からわかるように、::class を通じて取得できます。またはオブジェクトを介して直接インスタンス オブジェクトを取得することを示します。

IOCとは何ですか?

IOC コンテナについて聞いたことがあるかもしれませんが、IOC の正式名は (Inversion Of Control、反転制御) です。

制御の反転とは何かを理解しましょう。従来のコーディングでは、通常、クラス間の依存関係はコーディングを通じて新しいオブジェクトによって渡されますが、制御の反転を使用すると、オブジェクトの制御をコンテナーまたはフレームワークに渡すことができます。実装のために。その目的は、ハードコーディングなしでオブジェクトを作成できるようにすることであり、図 1 からわかるように、コンテナーには多数のオブジェクトが格納されており、使用したいときにそれらを直接使用できます。コンテナ内のオブジェクトをコードで作成する必要はありません。あるクラスのオブジェクトが必要な場合はコンテナからオブジェクトを取得し、オブジェクトが存在しない場合は自動的に作成されます。これは、コード内でオブジェクトを作成するプロセスを省略し、その作成プロセスをコンテナによって実現することを意味しており、これを反転制御と呼びます。 IOC を一言で要約すると、オブジェクト作成の制御をコンテナ実装クラスのインスタンス化に移します。

例: IOC を使用せずにクラスを作成したい場合

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

新しいクラスを手動で作成する必要がありますが、この場合、コード内にハードコーディングされています。

IOC コンテナを使用するコードは次のように記述できます。

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

このクラスをコンテナ内に実装するのを手伝ってください。これを見て質問する生徒もいるかもしれません。新しい Sunny を使用すれば、コードはより短くて簡単になるのではないでしょうか?依存関係の注入を見た後で例を見てみましょう。

Dependency Injection

IOC とは何かを理解したところで、新しい疑問が生じます。クラスを作成するときに、一部のクラスのコンストラクターがパラメーターを渡す必要がある場合はどうすればよいでしょうか? IOC の研究を通じて、IOC コンテナがオブジェクト インスタンスの作成の問題を解決するのに役立つことがわかりました。コンテナ内でオブジェクトを作成するときに、クラスに他の依存関係があることが判明した場合、依存関係の検索が実行されます。コンテナが必要なオブジェクトを見つけるプロセスは DL (Dependency Lookup、依存関係ルックアップ) と呼ばれます。必要な依存関係をコードフラグメントに注入することを DI (Dependency Injection、依存性注入) と呼びます。

たとえば、IOC で言及された新しい 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");

このコードから、キャッシュで使用されるドライバーが変更されると、コードは呼び出し側で記述され、結合度が高くなるため、キャッシュ コードも対応する変更を行う必要があることがわかります。これはコードを変更するのと同じで、プログラマがインターフェイスに合わせてプログラミングできるようになり、コードの汎用性と標準化が向上します。

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");

多くの人は、このコードを見て、目的のオブジェクトをコンストラクターに直接渡すべきではないかと考えます。なぜインターフェースを定義する必要があるのでしょうか?実際、インターフェースはコードを標準化するために定義されています。どのドライバーを使用しても、私のインターフェースを実装している限り、それを使用できます。インターフェースがないと、開発者は開発時にドライバーにどのようなメソッドを含めるべきかわかりません。ドライバ。インターフェースを使用する場合、インターフェースをプログラミングするだけでよく、キャッシュはクラスの実装方法には関係なく、インターフェースのメソッドに従って動作するだけです。

一文で要約: 疎結合を実現するための依存関係の反転

実践的な戦闘: コンテナーの原則に基づいたコンテナーの実装

<?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 コンテナを理解して実装するための 1 つの記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はIT不是挨踢微信公众号で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。