目次
サービス バインディング
インスタンス
bind
singleton
alias
サービス解析
ホームページ バックエンド開発 PHPチュートリアル Laravelサービスコンテナ(IocContainer)の解釈

Laravelサービスコンテナ(IocContainer)の解釈

Jul 06, 2018 pm 03:06 PM
laravel ソースコード分析

この記事では、Laravelのサービスコンテナ(IocContainer)の解釈を中心に紹介しますが、これは参考になると思いますので、皆さんにも共有しておきますので、困っている友達は参考にしてください

Laravelの核となるのはIocContainerです。ドキュメント 「サービスコンテナ」と呼ばれます サービスコンテナはクラスの依存関係を管理し、依存関係の注入を実行するための強力なツールです Route、Eloquent ORM、Request、ResponseなどのLaravelの機能モジュールは実際にはコアによって提供されます-独立したクラスモジュール。これらのクラスは、登録からインスタンス化までlaravelサービスコンテナを実際に担当し、最終的に私たちが使用します。

サービス コンテナとは何かについて明確なアイデアがない場合は、サービス コンテナの詳細について学ぶためのブログ投稿をお勧めします: laravel の魔法のサービス コンテナ

サービス コンテナには、制御の反転 (IOC) と依存関係の挿入 (DI) という 2 つの概念があります。

依存関係の挿入と制御の反転は、同じものについての異なる説明であり、異なる観点から説明します。 。依存関係の注入はアプリケーションの観点から説明されており、アプリケーションはコンテナーに依存して、必要な外部リソースを作成して注入します。制御の反転をコンテナの観点から説明すると、コンテナはアプリケーションを制御し、コンテナはアプリケーションが必要とする外部リソースをアプリケーションに逆注入します。

Laravel では、フレームワークがさまざまなサービスをサービス コンテナにバインドします。また、カスタム サービスをコンテナにバインドすることもできます。アプリケーションが特定のサービスを使用する必要がある場合、サービス コンテナはサービスを解決し、サービス間の依存関係を自動的に解決して、使用するためにアプリケーションに渡します。

この記事では、Laravel でサービス バインディングと解析がどのように実装されるかについて説明します。

サービス バインディング

サービスをコンテナにバインドするために一般的に使用されるメソッドには、instance、bind、singleton、alias などがあります。個別に見てみましょう。

インスタンス

既存のオブジェクトをサービス コンテナにバインドします。その後サービスが名前によって解決されると、コンテナは常にバインドされたインスタンスを返します。

$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\Api', $api);
ログイン後にコピー

サービスコンテナの$instnces属性にオブジェクトが登録されます

[
     'HelpSpot\Api' => $api//$api是API类的对象,这里简写了
 ]
ログイン後にコピー

bind

サービスをサービスコンテナにバインドします

3 つのバインド メソッド :

1.绑定自身
$this->app->bind('HelpSpot\API', null);

2.绑定闭包
$this->app->bind('HelpSpot\API', function () {
    return new HelpSpot\API();
});//闭包直接提供类实现方式
$this->app->bind('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});//闭包返回需要依赖注入的类
3. 绑定接口和实现
$this->app->bind('Illuminate\Tests\Container\IContainerContractStub', 'Illuminate\Tests\Container\ContainerImplementationStub');
ログイン後にコピー

最初のケースでは、実際、バインド メソッド内で、サービスをバインドする前に getClosure() を通じてサービスのクロージャが生成されます。バインドメソッドのソースコードで。

public function bind($abstract, $concrete = null, $shared = false)
{
    $abstract = $this->normalize($abstract);
    
    $concrete = $this->normalize($concrete);
    //如果$abstract为数组类似['Illuminate/ServiceName' => 'service_alias']
    //抽取别名"service_alias"并且注册到$aliases[]中
    //注意:数组绑定别名的方式在5.4中被移除,别名绑定请使用下面的alias方法
    if (is_array($abstract)) {
        list($abstract, $alias) = $this->extractAlias($abstract);

        $this->alias($abstract, $alias);
    }

    $this->dropStaleInstances($abstract);

    if (is_null($concrete)) {
        $concrete = $abstract;
    }
    //如果只提供$abstract,则在这里为其生成concrete闭包
    if (! $concrete instanceof Closure) {
        $concrete = $this->getClosure($abstract, $concrete);
    }

    $this->bindings[$abstract] = compact('concrete', 'shared');

    if ($this->resolved($abstract)) {
        $this->rebound($abstract);
    }
}


protected function getClosure($abstract, $concrete)
{
    // $c 就是$container,即服务容器,会在回调时传递给这个变量
    return function ($c, $parameters = []) use ($abstract, $concrete) {
        $method = ($abstract == $concrete) ? 'build' : 'make';

        return $c->$method($concrete, $parameters);
    };
}
ログイン後にコピー

bind は、次のようにサービスをサービス コンテナの $bindings 属性に登録します。

$bindings = [
    'HelpSpot\API' =>  [//闭包绑定
        'concrete' => function ($app, $paramters = []) {
            return $app->build('HelpSpot\API');
        },
        'shared' => false//如果是singleton绑定,这个值为true
    ]        
    'Illuminate\Tests\Container\IContainerContractStub' => [//接口实现绑定
        'concrete' => 'Illuminate\Tests\Container\ContainerImplementationStub',
        'shared' => false
    ]
]
ログイン後にコピー

singleton

public function singleton($abstract, $concrete = null)
{
    $this->bind($abstract, $concrete, true);
}
ログイン後にコピー

singleton メソッドは、bind メソッドのバリアントです。バインドするには、クラスまたはインターフェイスをコンテナーに一度解決するだけで済みます。その後、サービスはコンテナーへの後続の呼び出しに対して同じインスタンスを返します。

alias

サービスとサービス エイリアスを登録します。 thecontainer:

public function alias($abstract, $alias)
{
    $this->aliases[$alias] = $this->normalize($abstract);
}
ログイン後にコピー

alias メソッドは、上記のバインド メソッドで役立ち、サービス コンテナの $aliases 属性にサービス エイリアスとサービス クラスの対応関係を登録します。
例:

$this->app->alias('\Illuminate\ServiceName', 'service_alias');
ログイン後にコピー

サービスをバインドした後、サービス オブジェクトを使用するときに

$this->app->make('service_alias');
ログイン後にコピー

を通じてサービス オブジェクトを解析できるため、サービス オブジェクトを使用するときに長いクラス名を記述する必要がなくなります。 make メソッドの使用エクスペリエンスが大幅に向上しました。

サービス解析

make: サービス コンテナからサービス オブジェクトを解析します。このメソッドは解析したいクラス名またはインターフェイス名をパラメータとして受け取ります

/**
 * Resolve the given type from the container.
 *
 * @param  string  $abstract
 * @param  array   $parameters
 * @return mixed
 */
public function make($abstract, array $parameters = [])
{
    //getAlias方法会假定$abstract是绑定的别名,从$aliases找到映射的真实类型名
    //如果没有映射则$abstract即为真实类型名,将$abstract原样返回
    $abstract = $this->getAlias($this->normalize($abstract));

    // 如果服务是通过instance()方式绑定的,就直接解析返回绑定的service
    if (isset($this->instances[$abstract])) {
        return $this->instances[$abstract];
    }

    // 获取$abstract接口对应的$concrete(接口的实现)
    $concrete = $this->getConcrete($abstract);

    if ($this->isBuildable($concrete, $abstract)) {
        $object = $this->build($concrete, $parameters);
    } else {
        //如果时接口实现这种绑定方式,通过接口拿到实现后需要再make一次才能
        //满足isBuildable的条件 ($abstract === $concrete)
        $object = $this->make($concrete, $parameters);
    }

    foreach ($this->getExtenders($abstract) as $extender) {
        $object = $extender($object, $this);
    }

    //如果服务是以singleton方式注册进来的则,把构建好的服务对象放到$instances里,
    //避免下次使用时重新构建
    if ($this->isShared($abstract)) {
        $this->instances[$abstract] = $object;
    }

    $this->fireResolvingCallbacks($abstract, $object);

    $this->resolved[$abstract] = true;

    return $object;
}

protected function getConcrete($abstract)
{
    if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
        return $concrete;
    }

    // 如果是$abstract之前没有注册类实现到服务容器里,则服务容器会认为$abstract本身就是接口的类实现
    if (! isset($this->bindings[$abstract])) {
        return $abstract;
    }

    return $this->bindings[$abstract]['concrete'];
}

protected function isBuildable($concrete, $abstract)
{        
    return $concrete === $abstract || $concrete instanceof Closure;
}
ログイン後にコピー

を使用します。 make メソッドを整理すると、build メソッドの機能は解析されたサービス オブジェクトをビルドすることであることがわかりましたので、オブジェクトをビルドする具体的な処理を見てみましょう。 (PHP クラスのリフレクションは、サービスの依存関係注入を実装する構築プロセス中に使用されます)

public function build($concrete, array $parameters = [])
{
    // 如果是闭包直接执行闭包并返回(对应闭包绑定)
    if ($concrete instanceof Closure) {
        return $concrete($this, $parameters);
    }
    
    // 使用反射ReflectionClass来对实现类进行反向工程
    $reflector = new ReflectionClass($concrete);

    // 如果不能实例化,这应该是接口或抽象类,再或者就是构造函数是private的
    if (! $reflector->isInstantiable()) {
        if (! empty($this->buildStack)) {
            $previous = implode(', ', $this->buildStack);

            $message = "Target [$concrete] is not instantiable while building [$previous].";
        } else {
            $message = "Target [$concrete] is not instantiable.";
        }

        throw new BindingResolutionException($message);
    }

    $this->buildStack[] = $concrete;

    // 获取构造函数
    $constructor = $reflector->getConstructor();

    // 如果构造函数是空,说明没有任何依赖,直接new返回
    if (is_null($constructor)) {
        array_pop($this->buildStack);

        return new $concrete;
    }
    
    // 获取构造函数的依赖(形参),返回一组ReflectionParameter对象组成的数组表示每一个参数
    $dependencies = $constructor->getParameters();

    $parameters = $this->keyParametersByArgument(
        $dependencies, $parameters
    );

    // 构建构造函数需要的依赖
    $instances = $this->getDependencies(
        $dependencies, $parameters
    );

    array_pop($this->buildStack);

    return $reflector->newInstanceArgs($instances);
}

//获取依赖
protected function getDependencies(array $parameters, array $primitives = [])
{
    $dependencies = [];

    foreach ($parameters as $parameter) {
        $dependency = $parameter->getClass();

        // 某一依赖值在$primitives中(即build方法的$parameters参数)已提供
        // $parameter->name返回参数名
        if (array_key_exists($parameter->name, $primitives)) {
            $dependencies[] = $primitives[$parameter->name];
        } 
        elseif (is_null($dependency)) {
             // 参数的ReflectionClass为null,说明是基本类型,如'int','string'
            $dependencies[] = $this->resolveNonClass($parameter);
        } else {
             // 参数是一个类的对象, 则用resolveClass去把对象解析出来
            $dependencies[] = $this->resolveClass($parameter);
        }
    }

    return $dependencies;
}

//解析出依赖类的对象
protected function resolveClass(ReflectionParameter $parameter)
{
    try {
        // $parameter->getClass()->name返回的是类名(参数在typehint里声明的类型)
        // 然后递归继续make(在make时发现依赖类还有其他依赖,那么会继续make依赖的依赖
        // 直到所有依赖都被解决了build才结束)
        return $this->make($parameter->getClass()->name);
    } catch (BindingResolutionException $e) {
        if ($parameter->isOptional()) {
            return $parameter->getDefaultValue();
        }

        throw $e;
    }
}
ログイン後にコピー

サービス コンテナは、laravel の核心であり、依存関係注入を通じてオブジェクト間の相互依存関係を解決できます。制御反転の場合、特定の動作は外部で定義されます (Route、Eloquent、これらは外部モジュールであり、独自の動作仕様を定義します。サービス コンテナは、登録から使用のためのインスタンス化まで、これらのクラスを担当します)。

クラスをコンテナーによって抽出するには、まずクラスをコンテナーに登録する必要があります。 laravelではこのコンテナをサービスコンテナと呼んでいますので、サービスが必要な場合には、まずサービスを登録してコンテナにバインドする必要があり、サービスを提供し、コンテナにサービスをバインドするのがサービスプロバイダ(ServiceProvider)です。サービスプロバイダーは大きくregister(登録)とboot(ブート、初期化)の2つに分かれますが、スペースの関係でLaravelサービスプロバイダーの内容については別のLaravelコア解釈 - サービスプロバイダー(ServiceProvider)を参照してください。

上記がこの記事の全内容です。皆様の学習に少しでもお役に立てれば幸いです。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。

関連する推奨事項:

Laravel コア解釈リクエスト

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

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

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHP でオブジェクト リレーショナル マッピング (ORM) を使用してデータベース操作を簡素化するにはどうすればよいですか? PHP でオブジェクト リレーショナル マッピング (ORM) を使用してデータベース操作を簡素化するにはどうすればよいですか? May 07, 2024 am 08:39 AM

PHP でのデータベース操作は、オブジェクトをリレーショナル データベースにマップする ORM を使用して簡素化されます。 Laravel の EloquentORM を使用すると、オブジェクト指向構文を使用してデータベースと対話できます。モデル クラスを定義したり、Eloquent メソッドを使用したり、実際にブログ システムを構築したりすることで ORM を使用できます。

Laravel と CodeIgniter の最新バージョンの比較 Laravel と CodeIgniter の最新バージョンの比較 Jun 05, 2024 pm 05:29 PM

Laravel 9 と CodeIgniter 4 の最新バージョンでは、更新された機能と改善が提供されます。 Laravel9はMVCアーキテクチャを採用しており、データベース移行、認証、テンプレートエンジンなどの機能を提供します。 CodeIgniter4 は、HMVC アーキテクチャを使用してルーティング、ORM、およびキャッシュを提供します。パフォーマンスの面では、Laravel9 のサービスプロバイダーベースの設計パターンと CodeIgniter4 の軽量フレームワークにより、優れたパフォーマンスが得られます。実際のアプリケーションでは、Laravel9 は柔軟性と強力な機能を必要とする複雑なプロジェクトに適しており、CodeIgniter4 は迅速な開発や小規模なアプリケーションに適しています。

Laravel と CodeIgniter のデータ処理機能はどのように比較されますか? Laravel と CodeIgniter のデータ処理機能はどのように比較されますか? Jun 01, 2024 pm 01:34 PM

Laravel と CodeIgniter のデータ処理機能を比較します。 ORM: Laravel はクラスとオブジェクトのリレーショナル マッピングを提供する EloquentORM を使用しますが、CodeIgniter は ActiveRecord を使用してデータベース モデルを PHP クラスのサブクラスとして表します。クエリビルダー: Laravel には柔軟なチェーンクエリ API がありますが、CodeIgniter のクエリビルダーはよりシンプルで配列ベースです。データ検証: Laravel はカスタム検証ルールをサポートする Validator クラスを提供しますが、CodeIgniter には組み込みの検証関数が少なく、カスタム ルールの手動コーディングが必要です。実践例:ユーザー登録例はLarを示しています

Laravel - アーティザンコマンド Laravel - アーティザンコマンド Aug 27, 2024 am 10:51 AM

Laravel - アーティザン コマンド - Laravel 5.7 には、新しいコマンドを処理およびテストするための新しい方法が付属しています。これには職人コマンドをテストする新しい機能が含まれており、そのデモについては以下で説明します。

Laravel と CodeIgniter ではどちらが初心者に優しいでしょうか? Laravel と CodeIgniter ではどちらが初心者に優しいでしょうか? Jun 05, 2024 pm 07:50 PM

初心者にとって、CodeIgniter は学習曲線が緩やかで機能は少ないですが、基本的なニーズはカバーしています。 Laravel は幅広い機能セットを提供しますが、学習曲線はわずかに急になります。パフォーマンスの点では、Laravel と CodeIgniter はどちらも良好なパフォーマンスを示します。 Laravel にはより広範なドキュメントとアクティブなコミュニティ サポートがあり、CodeIgniter はよりシンプルで軽量で、強力なセキュリティ機能を備えています。ブログアプリケーションを構築する実際のケースでは、Laravel の EloquentORM を使用するとデータ操作が簡素化されますが、CodeIgniter ではより手動の構成が必要になります。

Laravel と CodeIgniter: 大規模プロジェクトにはどちらのフレームワークが適していますか? Laravel と CodeIgniter: 大規模プロジェクトにはどちらのフレームワークが適していますか? Jun 04, 2024 am 09:09 AM

大規模プロジェクト用のフレームワークを選択する場合、Laravel と CodeIgniter にはそれぞれ独自の利点があります。 Laravel はエンタープライズレベルのアプリケーション向けに設計されており、モジュール設計、依存関係の注入、強力な機能セットを提供します。 CodeIgniter は、速度と使いやすさを重視した、小規模から中規模のプロジェクトに適した軽量フレームワークです。複雑な要件と多数のユーザーを伴う大規模なプロジェクトには、Laravel のパワーとスケーラビリティがより適しています。単純なプロジェクトやリソースが限られている状況では、CodeIgniter の軽量で迅速な開発機能がより理想的です。

Laravel と CodeIgniter: 小規模プロジェクトにはどちらのフレームワークが適していますか? Laravel と CodeIgniter: 小規模プロジェクトにはどちらのフレームワークが適していますか? Jun 04, 2024 pm 05:29 PM

小規模なプロジェクトの場合、Laravel は強力な機能とセキュリティを必要とする大規模なプロジェクトに適しています。 CodeIgniter は、軽量さと使いやすさを必要とする非常に小規模なプロジェクトに適しています。

PHP エンタープライズ アプリケーション マイクロサービス アーキテクチャ設計に関する Q&A PHP エンタープライズ アプリケーション マイクロサービス アーキテクチャ設計に関する Q&A May 07, 2024 am 09:36 AM

マイクロサービス アーキテクチャは、PHP フレームワーク (Symfony や Laravel など) を使用してマイクロサービスを実装し、RESTful 原則と標準データ形式に従って API を設計します。マイクロサービスはメッセージ キュー、HTTP リクエスト、または gRPC を介して通信し、監視とトラブルシューティングに Prometheus や ELKStack などのツールを使用します。

See all articles