Gimli includes a powerful dependency injection container that makes it easy to manage object dependencies and promote loosely coupled, testable code. The DI container automatically resolves class dependencies and provides several ways to configure and retrieve services.
The Injector system consists of these key components:
You can access the Injector in several ways:
<?php
// Through the Application Registry
use Gimli\Application_Registry;
$Injector = Application_Registry::get()->Injector;
// Using the injector() helper function
use function Gimli\Injector\injector;
$Injector = injector();
// Through dependency injection
class MyService {
public function __construct(
protected Gimli\Injector\Injector_Interface $Injector
) {
// Injector is automatically provided
}
}
The Injector automatically resolves dependencies:
<?php
// Using the Injector directly
$Database = $Injector->resolve(Gimli\Database\Database::class);
// Using the helper function
use function Gimli\Injector\resolve;
$Database = resolve(Gimli\Database\Database::class);
When resolving a class, the Injector:
If you have an existing object instance you want to make available:
<?php
// Using the Injector directly
$Cache = new Cache($config);
$Injector->register(Cache::class, $Cache);
// Now any class that needs Cache will get this instance
$Service = $Injector->resolve(Service::class); // Service constructor gets $Cache
For services that need custom initialization:
<?php
// Using the Injector directly
$Injector->bind(Database::class, function() use ($config) {
return new Database(new PDO($config['dsn'], $config['user'], $config['pass']));
});
// Using the helper function
use function Gimli\Injector\bind;
bind(Logger::class, function() {
$logger = new Logger('app');
$logger->pushHandler(new StreamHandler('logs/app.log'));
return $logger;
});
Sometimes you need to pass specific dependencies:
<?php
// Pass specific arguments when resolving
$User = $Injector->resolve(User::class, ['id' => 123]);
// Using the helper function with manual dependencies
$Post = resolve(Post::class, ['author' => $user, 'title' => 'Hello World']);
When you need a new instance instead of reusing a singleton:
<?php
// Using the Injector directly
$newMailer = $Injector->resolveFresh(Mailer::class);
// Using the helper function
use function Gimli\Injector\resolve_fresh;
$newValidator = resolve_fresh(Validator::class, ['rules' => $customRules]);
Call a method with automatic dependency injection:
<?php
// Using the Injector directly
$result = $Injector->call(ReportGenerator::class, 'generate', ['format' => 'pdf']);
// Using the helper function
use function Gimli\Injector\call_method;
$result = call_method(ReportGenerator::class, 'generate', ['format' => 'pdf']);
The Injector will:
Modify a service after it’s been resolved:
<?php
// Using the Injector directly
$Injector->extends(Router::class, function($router) {
$router->addMiddleware(new AuthMiddleware());
return $router;
});
// Using the helper function
use function Gimli\Injector\extend_class;
extend_class(EventManager::class, function($eventManager) {
$eventManager->subscribe('app.start', function() {
// Do something when app starts
});
return $eventManager;
});
The Gimli Injector supports automatic dependency resolution (auto-wiring). When a class is requested, the Injector examines its constructor and automatically resolves each parameter:
<?php
// These classes will be automatically wired together
class UserRepository {
public function __construct(
protected Database $Database,
protected CacheService $Cache
) {
// Both $Database and $Cache are automatically injected
}
}
class UserController {
public function __construct(
protected UserRepository $UserRepository,
protected AuthService $Auth
) {
// UserRepository with its dependencies is injected automatically
// AuthService is injected automatically
}
}
// Just resolve the controller, and all dependencies are handled
$controller = resolve(UserController::class);
The Injector detects circular dependencies and throws an exception to prevent infinite loops:
// This would cause a circular dependency exception:
class A {
public function __construct(B $b) {}
}
class B {
public function __construct(A $a) {}
}
resolveFresh
only when you need distinct instances