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

構文はとても簡潔です。

ArticleFactoryEditController クラスのコンストラクタで宣言されたすべての依存関係は、いわゆるオートワイヤリングのおかげで 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 は単体では、設定の servicesparameters セクションだけを有効にします。searchdecoratordiinject などほかのセクションを使うには、まずその拡張を登録してください。そして設定の 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): boolisCreated(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が引き受けます。

バージョン: 3.x