DIコンテナの本質はクラスとファクトリーメソッドのリストであり、本プロジェクトでは
[クラス名 => [ファクトリー関数, シングルトンフラグ]] という連想配列を管理するというものである。
もしファクトリー関数が指定されていなければ、リフレクションを使用してコンストラクタ引数を解析して自動的に生成(オートワイアリング)する。
SmallDiContainer\DiContainer は、PHP 向けの軽量な DI(Dependency Injection: 依存性注入)コンテナです。自動的な依存関係の解決(オートワイヤリング)や、ファクトリー関数を用いた柔軟なインスタンス生成、シングルトンライフサイクルの管理に対応しています。
本ライブラリはSmallDiContainer.phpにて単一ファイルにて提供されているのでお使いのプロジェクトにコピペすればそのまま使えます。
- シングルトンパターンのコンテナ
DiContainer::GetInstance()を使用してアプリ全体で単一のコンテナインスタンスを共有できます。 - 自動依存関係解決 (Auto-Wiring) コンストラクタの型ヒントをもとに、依存しているクラスを自動的に再帰解決します。
- ファクトリー関数の登録 複雑な初期化処理が必要なクラスには、クロージャを用いたカスタムファクトリー関数を指定可能です(厳格な型チェック機能付き)。
- ライフサイクル管理
Transient(解決ごとに新規生成)とSingleton(初回生成時のインスタンスを再利用)を切り替えられます。
コンテナのインスタンスは GetInstance() メソッドを通じて取得します。
use SmallDiContainer\DiContainer;
$container = DiContainer::GetInstance();
Register メソッドを使用して、コンテナにクラスを登録します。
public function Register(string $className, bool $isSingleton = false, ?\Closure $factory = null): void
$className(string): 登録するクラスの完全修飾名 (Class::class)。$isSingleton(bool):trueの場合はシングルトンとして扱われ、falseの場合は参照ごとに新しいインスタンスが生成されます(デフォルト:false)。$factory(?\Closure): 独自のインスタンス化ロジックが必要な場合に指定します(デフォルト:null)。
Resolve メソッドを使用して、登録されたクラスのインスタンスを取得します。
$service = $container->Resolve(UserService::class);
コンストラクタで他のクラスに依存している場合、コンテナが自動的に依存クラスを解決して注入します。
namespace App;
use SmallDiContainer\DiContainer;
class Logger {
public function log(string $message) {
echo "[LOG]: {$message}\n";
}
}
class UserRepository {
public function __construct(private Logger $logger) {}
public function find(int $id) {
$this->logger->log("User {$id} を取得しました");
return ['id' => $id, 'name' => 'Alice'];
}
}
$container = DiContainer::GetInstance();
// 依存される側のクラスと、依存する側のクラスの両方を登録
$container->Register(Logger::class);
$container->Register(UserRepository::class);
// UserRepository を取得すると、内部で自動的に Logger が注入されます
$userRepository = $container->Resolve(UserRepository::class);
$userRepository->find(1);
$isSingleton に true を指定することで、常に同じインスタンスを返すように設定できます。
$container->Register(DatabaseConnection::class, true);
$db1 = $container->Resolve(DatabaseConnection::class);
$db2 = $container->Resolve(DatabaseConnection::class);
// $db1 と $db2 は完全に行一のインスタンスになります
var_dump($db1 === $db2); // bool(true)
複雑な初期化(環境変数の読み込みや特定オプションの付与など)が必要な場合、ファクトリー関数(クロージャ)を渡すことができます。
ファクトリー関数を指定する場合、厳格なシグネチャに従う必要があります:
- 第1引数:
DiContainer型の引数を1つ受け取ること - 戻り値の型: 登録する
$classNameと一致する型ヒントを指定すること
class ApiClient {
public function __construct(public string $apiKey, public Logger $logger) {}
}
$container->Register(Logger::class);
// ファクトリー関数の登録例
$container->Register(
ApiClient::class,
false, // シングルトンフラグ
function (DiContainer $c): ApiClient {
// コンテナ経由で他の依存関係を取得可能
$logger = $c->Resolve(Logger::class);
$apiKey = "SECRET_API_KEY_12345";
return new ApiClient($apiKey, $logger);
}
);
$apiClient = $container->Resolve(ApiClient::class);
以下のケースでは \Exception がスローされます。
- 未登録のクラスを参照した場合
Resolve()にコンテナへ登録されていないクラス名を渡した際。 - ファクトリー関数のシグネチャ違反
Register()の際、ファクトリー関数の「引数の型・数」または「戻り値の型ヒント」が規則を満たしていない場合。