bnf/di
Lightweight dependency injection container for PHP/Laravel projects. Configure bindings and resolve services automatically with simple, minimal setup—ideal for small apps or packages that need clean inversion of control without a heavy framework.
Installation
composer require bnf/di
Add the container to your Laravel service provider (e.g., AppServiceProvider):
use Bnf\Di\Container;
public function register()
{
$this->app->singleton(Container::class, fn() => new Container());
}
Basic Usage Bind a class to an interface or concrete type:
$container = app(Container::class);
$container->bind(LoggerInterface::class, Logger::class);
First Use Case Resolve a dependency in a controller or service:
public function __construct(private LoggerInterface $logger)
{
// $logger is automatically resolved by the container
}
README.md for basic binding/resolution syntax.get() and has() methods.app(Container::class) to access the container globally.Singleton vs Transient
bind() for transient dependencies (new instance per resolution).singleton() for shared instances (e.g., database connections, config services):
$container->singleton(ConfigService::class, fn() => new ConfigService());
Abstract Binding Bind interfaces to concrete implementations:
$container->bind(RepositoryInterface::class, UserRepository::class);
Contextual Binding Resolve dependencies conditionally (e.g., based on request data):
$container->bindWhen(
CacheDriver::class,
fn($context) => $context['env'] === 'production' ? RedisDriver::class : FileDriver::class,
['env' => 'production']
);
Service Layer Integration
ServiceProvider:
public function register()
{
$this->app->make(Container::class)->bind(
PaymentGateway::class,
StripeGateway::class
);
}
Middleware/Events Resolve dependencies in closures:
$container->call([$this->mailer, 'send'], ['user@example.com']);
Testing Override bindings in tests:
$container->bind(MockLogger::class, MockLogger::class);
$this->app->instance(LoggerInterface::class, new MockLogger());
bnf/di alongside Laravel’s container for hybrid setups:
$this->app->extend(Container::class, fn($c) => new Container($c));
// In config/app.php
'providers' => [
Bnf\Di\DiServiceProvider::class, // Hypothetical provider
];
Circular Dependencies
ClassA to ClassB and ClassB to ClassA. Use setters or constructor injection carefully.Overriding Laravel Bindings
replace() or rebind():
$container->rebind(LoggerInterface::class, NewLogger::class);
Singleton Scope Leaks
$container->forget(LoggerInterface::class);
Resolution Errors
$container->bound(LoggerInterface::class); // bool
$container->getBindings(); // array of all bindings
dump($container->get(LoggerInterface::class)) to inspect resolved instances.Lazy Loading
$container->bind(HeavyService::class, fn() => new HeavyService(config('app.debug')));
Custom Resolvers Extend the container with a resolver:
$container->addResolver(fn($concrete) => new $concrete());
Contextual Resolution Pass context to bindings (e.g., request data):
$container->bindWhen(
User::class,
fn($context) => $context['id'] ? User::find($context['id']) : new User(),
['id' => 1]
);
Integration with Laravel Events Bind event listeners dynamically:
$container->bind(Handler::class, fn() => new Handler(app('events')));
bnf/di has no built-in config file. Define bindings programmatically.rebind() instead of bind() to replace existing bindings safely.How can I help you explore Laravel packages today?