Service container, dependency injection, service providers, and service discovery. These classes provide comprehensive dependency management, automatic service resolution, and lifecycle management for your Forge Engine applications.
The main dependency injection container that manages service registration, resolution, and lifecycle. The Container class provides automatic dependency resolution, singleton management, and service provider support.
Register a binding in the container.
$container = new Container();
// Bind an interface to an implementation
$container->bind(UserRepositoryInterface::class, UserRepository::class);
// Bind with a closure
$container->bind('mailer', function($container) {
    return new Mailer($container->make('config'));
});
// Bind as singleton
$container->bind('cache', Cache::class, true);Register a shared binding in the container.
// Register a singleton
$container->singleton('config', Config::class);
$container->singleton('db', DatabaseConnection::class);Register an existing instance as shared in the container.
// Register an existing instance
$config = new Config(['app.name' => 'My App']);
$container->instance('config', $config);Resolve the given type from the container.
// Resolve a class
$userRepository = $container->make(UserRepository::class);
// Resolve with parameters
$mailer = $container->make(Mailer::class, ['driver' => 'smtp']);
// Resolve an interface (will return bound implementation)
$repository = $container->make(UserRepositoryInterface::class);Alias for make() - PSR-11 container interface.
$service = $container->get('mailer');Check if the container has a binding.
if ($container->has('mailer')) {
    $mailer = $container->get('mailer');
}Register a conditional binding.
$container->when(true, function($container) {
    $container->bind('cache', RedisCache::class);
})->when(false, function($container) {
    $container->bind('cache', FileCache::class);
});Extend a binding with additional functionality.
$container->extend('mailer', function($mailer, $container) {
    $mailer->setLogger($container->get('logger'));
    return $mailer;
});Tag a binding or group of bindings.
$container->tag([UserRepository::class, PostRepository::class], 'repositories');
$container->tag([Cache::class, Session::class], 'caches');Get all services tagged with a specific tag.
$repositories = $container->tagged('repositories');
foreach ($repositories as $repository) {
    $repository->initialize();
}Base class for service providers that register services and boot functionality in the container. Service providers are the primary way to bootstrap your application services and dependencies.
Extend the base ServiceProvider class to create your own providers.
class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Register services in the container
        $this->container->singleton('config', Config::class);
        $this->container->bind(UserRepositoryInterface::class, UserRepository::class);
        
        // Register with closures
        $this->container->bind('mailer', function($container) {
            return new Mailer($container->get('config'));
        });
    }
    
    public function boot(): void
    {
        // Boot functionality after all services are registered
        $this->loadViews();
        $this->loadTranslations();
        $this->registerCommands();
    }
    
    protected function loadViews(): void
    {
        // Load view templates
        View::addPath(__DIR__ . '/../views');
    }
    
    protected function registerCommands(): void
    {
        // Register console commands
        $this->container->bind('command.migrate', MigrateCommand::class);
        $this->container->bind('command.seed', SeedCommand::class);
    }
}Register services in the container. This method is called first.
public function register(): void
{
    // Only register services, don't use them yet
    $this->container->bind('logger', Logger::class);
    $this->container->bind('cache', Cache::class);
}Boot the provider after all services are registered.
public function boot(): void
{
    // Now you can use services from the container
    $logger = $this->container->get('logger');
    $logger->info('Application booted');
    
    // Register event listeners
    $this->registerEventListeners();
}Return the services provided by this provider.
public function provides(): array
{
    return [
        'config',
        'logger',
        'cache',
        UserRepositoryInterface::class
    ];
}Automatically discovers and registers services, event listeners, and middleware from your application. This class provides convention-based service discovery to reduce manual configuration.
Discover and register services from a namespace.
$discoverer = new ServiceDiscoverSetup($container);
// Discover services from App\Services namespace
$discoverer->discoverServices('App\\Services', __DIR__ . '/../app/Services');
// Discover repository services
$discoverer->discoverServices('App\\Repositories', __DIR__ . '/../app/Repositories');Discover and register event listeners.
// Discover event listeners
$discoverer->discoverListeners('App\\Listeners', __DIR__ . '/../app/Listeners');
// Example listener that would be discovered
namespace App\Listeners;
class UserRegisteredListener
{
    public function handle(UserRegisteredEvent $event): void
    {
        // Send welcome email
        $this->sendWelcomeEmail($event->user);
    }
    
    private function sendWelcomeEmail(User $user): void
    {
        // Email sending logic
    }
}Discover and register middleware classes.
// Discover middleware
$discoverer->discoverMiddleware('App\\Middleware', __DIR__ . '/../app/Middleware');
// Example middleware that would be discovered
namespace App\Middleware;
use Forge\Http\Request;
use Forge\Http\Response;
class AuthMiddleware
{
    public function handle(Request $request, callable $next): Response
    {
        if (!$request->user()) {
            return new Response('Unauthorized', 401);
        }
        
        return $next($request);
    }
}Common patterns and examples for working with the Dependency Injection Container.
// Create container
$container = new Container();
// Register simple bindings
$container->bind('logger', Logger::class);
$container->singleton('config', Config::class);
// Register with closures
$container->bind('mailer', function($container) {
    return new Mailer(
        $container->get('config')->get('mail'),
        $container->get('logger')
    );
});
// Resolve services
$logger = $container->get('logger');
$config = $container->get('config');
$mailer = $container->get('mailer');// Define interfaces
interface UserRepositoryInterface
{
    public function find($id);
    public function create(array $data);
}
interface CacheInterface
{
    public function get($key);
    public function set($key, $value, $ttl = null);
}
// Bind interfaces to implementations
$container->bind(UserRepositoryInterface::class, UserRepository::class);
$container->bind(CacheInterface::class, RedisCache::class);
// Constructor injection
class UserService
{
    private $repository;
    private $cache;
    
    public function __construct(
        UserRepositoryInterface $repository,
        CacheInterface $cache
    ) {
        $this->repository = $repository;
        $this->cache = $cache;
    }
    
    public function getUser($id)
    {
        $cacheKey = "user.{$id}";
        
        if ($cached = $this->cache->get($cacheKey)) {
            return $cached;
        }
        
        $user = $this->repository->find($id);
        $this->cache->set($cacheKey, $user, 3600);
        
        return $user;
    }
}
// Resolve with automatic injection
$userService = $container->make(UserService::class);// Application bootstrap
class Application
{
    protected $container;
    protected $providers = [];
    
    public function __construct()
    {
        $this->container = new Container();
        $this->registerBaseProviders();
        $this->registerApplicationProviders();
    }
    
    protected function registerBaseProviders(): void
    {
        $this->register(new ConfigServiceProvider($this->container));
        $this->register(new LoggingServiceProvider($this->container));
        $this->register(new DatabaseServiceProvider($this->container));
    }
    
    protected function registerApplicationProviders(): void
    {
        $this->register(new AppServiceProvider($this->container));
        $this->register(new EventServiceProvider($this->container));
        $this->register(new RouteServiceProvider($this->container));
    }
    
    public function register(ServiceProvider $provider): void
    {
        $provider->register();
        $this->providers[] = $provider;
    }
    
    public function boot(): void
    {
        foreach ($this->providers as $provider) {
            $provider->boot();
        }
    }
    
    public function getContainer(): Container
    {
        return $this->container;
    }
}
// Usage
$app = new Application();
$app->boot();
// Get services from container
$logger = $app->getContainer()->get('logger');
$db = $app->getContainer()->get('db');// Register tagged services
$container->bind('cache.redis', RedisCache::class);
$container->bind('cache.file', FileCache::class);
$container->bind('cache.memory', MemoryCache::class);
$container->tag(['cache.redis', 'cache.file', 'cache.memory'], 'cache.drivers');
// Get all tagged services
$cacheDrivers = $container->tagged('cache.drivers');
// Use in a cache manager
class CacheManager
{
    protected $drivers = [];
    protected $default;
    
    public function __construct(array $drivers, string $default)
    {
        $this->drivers = $drivers;
        $this->default = $default;
    }
    
    public function driver(string $name = null)
    {
        $name = $name ?: $this->default;
        
        if (!isset($this->drivers[$name])) {
            throw new InvalidArgumentException("Cache driver [{$name}] not found.");
        }
        
        return $this->drivers[$name];
    }
    
    public function getDefaultDriver(): string
    {
        return $this->default;
    }
}
// Register cache manager
$container->bind('cache.manager', function($container) {
    $drivers = $container->tagged('cache.drivers');
    $default = $container->get('config')->get('cache.default', 'file');
    
    return new CacheManager($drivers, $default);
});Recommended patterns and guidelines for working with the Dependency Injection Container.