Nette DI Container
Nette DI は Nette の最も興味深いライブラリのひとつです。きわめて高速で、驚くほど設定しやすいコンパイル済みの DI コンテナを生成し、自動的に更新できます。
DI コンテナが作るべきサービスの形は、ふつう NEON 形式の設定ファイルで定義します。前の章で手作りしたコンテナは、次のように書けます。
parameters:
db:
dsn: 'mysql:'
user: root
password: '***'
services:
- Nette\Database\Connection(%db.dsn%, %db.user%, %db.password%)
- ArticleFactory
- EditController
構文はとても簡潔です。
ArticleFactory と EditController
クラスのコンストラクタで宣言されたすべての依存関係は、いわゆるオートワイヤリングのおかげで Nette DI
が見つけて自動的に渡すので、設定ファイルに何も書く必要はありません。ですからパラメータが変わっても、設定を変える必要はありません。開発中は
Nette
がコンテナを自動的に作り直します。あなたはアプリケーションの開発だけに集中できます。
セッターで依存関係を渡したい場合は、そのために setupセクションを使います。
Nette DI はコンテナの PHP コードを直接生成します。ですから結果は .php
ファイルで、開いて中を確かめられます。コンテナがどう機能するかを正確に見られるわけです。IDE
でデバッグして、実行を追うこともできます。そして何より、生成された PHP
コードはきわめて高速です。
Nette DI は、与えられたインターフェースにもとづいてファクトリのコードを生成することもできます。ですから
ArticleFactory
クラスの代わりに、アプリケーションではインターフェースを作るだけで済みます。
interface ArticleFactory
{
function create(): Article;
}
完全な例は GitHubにあります。
単体での利用
Nette DI ライブラリをアプリケーションに組み込むのはとても簡単です。まず Composer でインストールします(zip ファイルをダウンロードするのは、もう時代遅れですから)。
composer require nette/di
次のコードは Compilerを使い、config.neon
ファイルに書かれた設定に従って DI コンテナのインスタンスを作ります。
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp');
$class = $loader->load(function ($compiler) {
$compiler->loadConfig(__DIR__ . '/config.neon');
});
$container = new $class;
コンテナが生成されるのは一度きりで、そのコードはキャッシュ(__DIR__ . '/temp'
ディレクトリ)に書かれ、以降のリクエストではそこから読み込まれるだけです。
Compiler は単体では、設定の services と parameters
セクションだけを有効にします。search、decorator、di、inject
などほかのセクションを使うには、まずその拡張を登録してください。そして設定の
extensions
セクションから拡張を登録できるようにするには、ExtensionsExtension を足します。
$compiler->addExtension('search', new Nette\DI\Extensions\SearchExtension($tempDir));
$compiler->addExtension('extensions', new Nette\DI\Extensions\ExtensionsExtension);
完全な Nette のアプリケーションで使われる Configuratorは、これらをすべて自動的に登録します。
同じキャッシュディレクトリに複数の異なるコンテナを置く場合は、load() の第 2
引数に渡すキーで区別してください。それは生成されるクラス名の一部になります。
$class = $loader->load(
fn($compiler) => $compiler->loadConfig(__DIR__ . '/config.neon'),
'my-key',
);
サービスの生成と取得には getService() や getByType()
メソッドを使います。EditController オブジェクトはこうして作ります。
$controller = $container->getByType(EditController::class);
$controller->someMethod();
開発中は自動リフレッシュのモードを有効にすると便利です。クラスや設定ファイルが変わると、コンテナが自動的に作り直されます。ContainerLoaderのコンストラクタの第 2 引数に
true を渡すだけです。
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp', true);
コンテナを扱う
getService() と getByType()
のほかにも、コンテナのオブジェクトには便利なメソッドがいくつかあります。
getByType(string $type, bool $throw = true): ?objectは指定した型のサービスを返します。第 2 引数にfalseを渡すと、そのサービスがない場合に例外を投げる代わりにnullを返します。hasService(string $name): boolとisCreated(string $name): boolは、サービスが定義されているか、そしてすでに生成されているかを教えてくれます。getParameters(): arrayはコンテナのすべてのパラメータを、getParameter($key)はひとつのパラメータを返します。createInstance(string $class, array $args = []): objectは指定したクラスの新しいインスタンスを作り、そのコンストラクタの依存関係をオートワイヤリングで渡します。callMethod(callable $function, array $args = []): mixedは指定した callable を呼び、その引数をオートワイヤリングで渡します。callInjects(object $service): voidは指定したオブジェクトのすべてのinject*()メソッドを呼び、依存関係を渡します。
コンテナのコンストラクタは、設定で定義されたものを補うパラメータの配列も受け取れます。
$container = new $class(['host' => 'localhost']);
Nette Framework との併用
ここまで見てきたとおり、Nette DI の利用は Nette Framework で作られたアプリケーションに限られません。3 行のコードでどこにでも組み込めます。とはいえ Nette Framework でアプリケーションを開発しているなら、コンテナの設定と生成は Bootstrapが引き受けます。