LaravelのIoCコンテナの詳細

WBOY
WBOYオリジナル
2023-09-01 18:49:101387ブラウズ

深入探索 Laravel 的 IoC 容器

制御の反転 (IoC) は、従来の手続き型コードと比較して制御の反転を可能にする手法です。もちろん、IoC の最も顕著な形式は依存性注入 (DI) です。 Laravel の IoC コンテナは、最もよく使用される Laravel 機能の 1 つですが、おそらく最も理解されていません。

これは、依存関係注入を使用して制御の反転を実装する非常に簡単な例です。

リーリー

コンストラクター インジェクションを使用することで、Petrol インスタンスの作成を呼び出し元自体に委任し、制御の反転を実現します。私たちの JeepWrangler は、ガソリン がどこから来たのかを知る必要はありません。ただ入手するだけです。

それでは、これは Laravel と何の関係があるのでしょうか?実際にはかなりたくさんあります。知らなかった方のために説明しますが、Laravel は実際には IoC コンテナです。ご想像のとおり、コンテナは のものを含む オブジェクトです。 Laravel の IoC コンテナは、さまざまなバインディングを含めるために使用されます。 Laravel で行うことはすべて、ある時点で IoC コンテナと対話します。この相互作用は通常、バインディングが解決されるという形をとります。

既存の Laravel サービスプロバイダーを開くと、おそらく register メソッドに同様の内容が表示されるでしょう (例は大幅に簡略化されています)。

リーリー

これは非常に基本的なバインディングです。これは、バインディングの名前 (router) とパーサー (クロージャ) で構成されます。バインディングがコンテナから解決されると、Router のインスタンスが返されます。

Laravel は、sessionsession.store など、類似したバインディング名をグループ化することがよくあります。

バインディングを解決するには、メソッドを直接呼び出すか、コンテナ上で make メソッドを使用します。

リーリー

これは、コンテナーの最も基本的な形式での動作です。ただし、Laravel のほとんどの機能と同様に、単にクラスをバインドして解決するだけではありません。

共有バインディングと非共有バインディング

Laravel サービスプロバイダーのいくつかを参照すると、ほとんどのバインディングが前の例と同様に定義されていることがわかります。ああ、またか:######### リーリー

このバインディングは、コンテナーで

share

メソッドを使用します。 Laravel は静的変数を使用して以前に解決された値を保存し、バインディングが再度解決されるときにその値を単純に再利用します。これは基本的に

share メソッドが行うことです。 リーリー これを記述するもう 1 つの方法は、bindShared

メソッドを使用することです。

リーリー singleton

メソッドと

instance メソッドを使用して共有バインディングを実装することもできます。では、両方が同じ目標を達成する場合、違いは何でしょうか?実際にはそれほど多くはありません。私は個人的には bindShared メソッドを使用することを好みます。 条件付きバインディング

何かをコンテナにバインドしたい場合がありますが、これはそれが以前にバインドされていない場合に限られます。この問題を解決するにはいくつかの方法がありますが、最も簡単な方法は

bindIf メソッドを使用することです。

リーリー

router

バインディングがまだ存在しない場合にのみ、コンテナにバインドされます。ここで注意すべき唯一の点は、条件付きバインディングがどのように共有されるかということです。これを行うには、

bindIf メソッドの 3 番目のパラメーターに値 true を指定する必要があります。 自動依存関係解決

IoC コンテナの最も一般的に使用される機能の 1 つは、バインドされていないクラスの依存関係を自動的に解決する機能です。これはどういう意味ですか?まず、実際にはインスタンスを解決するために何かをコンテナにバインドする必要はありません。ほぼすべてのクラスのインスタンスを

make するだけで済みます。

リーリー

コンテナは、

Petrol

クラスをインスタンス化します。最も良い点は、コンストラクターの依存関係も解決してくれることです。

リーリー コンテナが最初に行うことは、JeepWrangler

クラスの依存関係をチェックすることです。次に、これらの依存関係を解決しようとします。したがって、

JeepWrangler タイプは Petrol クラスを示唆しているため、コンテナーは自動的にそれを解決し、依存関係として挿入します。 コンテナーは、タイプヒントではない依存関係を自動的に挿入できません。したがって、依存関係の 1 つが配列である場合は、それを手動でインスタンス化するか、パラメーターのデフォルト値を指定する必要があります。

バインディングの実装

Laravel で依存関係を自動的に解決できるのは非常に優れており、手動でクラスをインスタンス化するプロセスが簡素化されます。ただし、特にインターフェイスを使用する場合には、特定の実装を挿入する必要がある場合があります。これは、クラスの完全修飾名をバインディングとして使用することで簡単に実現できます。これを実証するために、

Fuel という新しいインターフェイスを使用します。

interface Fuel
{
    public function getPrice();
}

现在我们的 JeepWrangler 类可以对接口进行类型提示,并且我们将确保我们的 Petrol 类实现该接口。

class JeepWrangler
{
    public function __construct(Fuel $fuel)
    {
        $this->fuel = $fuel;
    }
	
    public function refuel($litres)
    {
        return $litres * $this->fuel->getPrice();
    }
}

class Petrol implements Fuel
{
    public function getPrice()
    {
        return 130.7;
    }
}

现在,我们可以将 Fuel 接口绑定到容器,并让它解析 Petrol 的新实例。

$this->app->bind('Fuel', 'Petrol');

// Or, we could instantiate it ourselves.
$this->app->bind('Fuel', function ($app) {
    return new Petrol;
});

现在,当我们创建 JeepWrangler 的新实例时,容器会看到它请求 Fuel ,并且它会知道自动注入 Petrol

这也使得更换实现变得非常容易,因为我们可以简单地更改容器中的绑定。为了进行演示,我们可能会开始使用优质汽油为汽车加油,这种汽油价格稍贵一些。

class PremiumPetrol implements Fuel
{
    public function getPrice()
    {
        return 144.3;
    }
}

// In our service provider...
$this->app->bind('Fuel', 'PremiumPetrol');

上下文绑定

请注意,上下文绑定仅在 Laravel 5 中可用。

上下文绑定允许您将实现(就像我们上面所做的那样)绑定到特定的类。

abstract class Car
{
    public function __construct(Fuel $fuel)
    {
        $this->fuel = $fuel;
    }

    public function refuel($litres)
    {
        return $litres * $this->fuel->getPrice();
    }
}

然后,我们将创建一个新的 NissanPatrol 类来扩展抽象类,并且我们将更新 JeepWrangler 来扩展它。

class JeepWrangler extends Car
{
    //
}

class NissanPatrol extends Car
{
    //
}

最后,我们将创建一个新的 Diesel 类,该类实现 Fuel 接口。

class Diesel implements Fuel
{
    public function getPrice()
    {
        return 135.3;
    }
}

现在,我们的吉普牧马人将使用汽油加油,我们的日产途乐将使用柴油加油。如果我们尝试使用与之前相同的方法,将实现绑定到接口,那么这两辆车都会获得相同类型的燃料,这不是我们想要的。

因此,为了确保每辆车都使用正确的燃料加油,我们可以通知容器在每种情况下使用哪种实现。

$this->app->when('JeepWrangler')->needs('Fuel')->give('Petrol');
$this->app->when('NissanPatrol')->needs('Fuel')->give('Diesel');

标记

请注意,标记仅在 Laravel 5 中可用。

能够解析容器中的绑定非常重要。通常,只有知道某些内容如何绑定到容器时,我们才能解决该问题。在 Laravel 5 中,我们现在可以为绑定添加标签,以便开发人员可以轻松解析具有相同标签的所有绑定。

如果您正在开发一个允许其他开发人员构建插件的应用程序,并且您希望能够轻松解析所有这些插件,那么标签将非常有用。

$this->app->tag('awesome.plugin', 'plugin');

// Or an array of tags.

$tags = ['plugin', 'theme'];

$this->app->tag('awesome.plugin', $tags);

现在,要解析给定标记的所有绑定,我们可以使用 tagged 方法。

$plugins = $this->app->tagged('plugin');

foreach ($plugins as $plugin) {
    $plugin->doSomethingFunky();
}

篮板和重新绑定

当您将某些内容多次绑定到同名容器时,称为重新绑定。 Laravel 会注意到你再次绑定了一些东西并会触发反弹。

这里最大的好处是当您开发一个包时,允许其他开发人员通过重新绑定容器中的组件来扩展它。要使用它,我们需要在 Car 摘要上实现 setter 注入。

abstract class Car
{
    public function __construct(Fuel $fuel)
    {
        $this->fuel = $fuel;
    }

    public function refuel($litres)
    {
        return $litres * $this->fuel->getPrice();
    }
    
    public function setFuel(Fuel $fuel)
    {
        $this->fuel = $fuel;
    }
    
}

假设我们将 JeepWrangler 像这样绑定到容器。

$this->app->bindShared('fuel', function ($app) {
    return new Petrol;
});

$this->app->bindShared('car', function ($app) {
	return new JeepWrangler($app['fuel']);
});

这完全没问题,但假设另一位开发人员出现并希望扩展此功能并在汽车中使用优质汽油。因此,他们使用 setFuel 方法将新燃料注入汽车。

$this->app['car']->setFuel(new PremiumPetrol);

在大多数情况下,这可能就是所需要的;但是,如果我们的包变得更加复杂并且 fuel 绑定被注入到其他几个类中怎么办?这将导致其他开发人员必须多次设置他们的新实例。因此,为了解决这个问题,我们可以利用重新绑定:

$this->app->bindShared('car', function ($app) {
    return new JeepWrangler($app->rebinding('fuel', function ($app, $fuel) {
		$app['car']->setFuel($fuel);
	}));
});

重新绑定 方法将立即返回给我们已经绑定的实例,以便我们能够在 JeepWrangler 的构造函数中使用它。提供给 rebinding 方法的闭包接收两个参数,第一个是 IoC 容器,第二个是新绑定。然后,我们可以自己使用 setFuel 方法将新绑定注入到我们的 JeepWrangler 实例中。

剩下的就是其他开发人员只需在容器中重新绑定 fuel 即可。他们的服务提供商可能如下所示:

$this->app->bindShared('fuel', function () {
    return new PremiumPetrol;
});

一旦绑定在容器中反弹,Laravel 将自动触发关联的闭包。在我们的示例中,新的 PremiumPetrol 实例将在我们的 JeepWrangler 实例上设置。

扩展

如果您想将依赖项注入核心绑定之一或由包创建的绑定,那么容器上的 extend 方法是最简单的方法之一。

此方法将解析来自容器的绑定,并以容器和解析的实例作为参数执行闭包。这使您可以轻松解析和注入您自己的绑定,或者简单地实例化一个新类并注入它。

$this->app->extend('car', function ($app, $car) {
    $car->setFuel(new PremiumPetrol);
});

与重新绑定不同,这只会设置对单个绑定的依赖关系。

Laravel 之外的使用

与构成 Laravel 框架的许多 Illuminate 组件一样,Container 可以在 Laravel 之外的独立应用程序中使用。为此,您必须首先将其作为 composer.json 文件中的依赖项。

{
    "require": {
        "illuminate/container": "4.2.*"
   }
}

这将安装容器的最新 4.2 版本。现在,剩下要做的就是实例化一个新容器。

require 'vendor/autoload.php';

$app = new Illuminate\Container\Container;

$app->bindShared('car', function () {
    return new JeepWrangler;
});

在所有组件中,当您需要灵活且功能齐全的 IoC 容器时,这是最容易使用的组件之一。

以上がLaravelのIoCコンテナの詳細の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。