nette/di
Nette DI is a fast, configurable dependency injection container for PHP. Compile-time container generation boosts performance, while extensions, autowiring, and service definitions make complex apps easy to wire. Integrates smoothly with the Nette framework or standalone.
Installation:
composer require nette/di
Add to your composer.json under autoload-dev for development:
"autoload-dev": {
"psr-4": {
"App\\": "src/"
}
}
Basic Container Initialization:
use Nette\DI\ContainerLoader;
use Nette\DI\Container;
$loader = new ContainerLoader($tempDir, $mode);
$container = $loader->load(function ($container) {
// Define services here
$container->addService('logger', new \Monolog\Logger('app'));
});
First Use Case: Define a service and inject it into a class:
$container->addService('mailer', new \App\Mailer());
$mailer = $container->getService('mailer');
src/ – Core classes like Container, ContainerBuilder, and ServiceDefinition.tests/ – Real-world usage examples and edge cases.examples/ – Quick demos for autowiring, decorators, and extensions.Leverage PHP type hints for automatic dependency resolution:
// In your config (Neon/YAML or PHP)
services:
- App\Services\UserService
- App\Services\LoggerService
// No manual binding needed; DI resolves dependencies via constructor/type hints.
Use Neon/YAML for declarative service definitions:
services:
database:
type: PDO
arguments:
- 'mysql:host=localhost;dbname=test'
- 'user'
- 'pass'
repository:
type: App\Repositories\UserRepository
arguments:
database: @database
Modularize container setup with extensions:
use Nette\DI\CompilerExtension;
class MyExtension extends CompilerExtension {
public function loadConfiguration() {
$builder = $this->getContainerBuilder();
$builder->addService('myService', new \App\MyService());
}
}
Register in bootstrap.php:
$container->addExtension(new MyExtension());
Delay initialization until first use (PHP 8.4+):
$container->addLazyService('expensiveService', function () {
return new \App\ExpensiveService();
});
Wrap services dynamically:
services:
logger:
type: App\Services\Logger
decorate: true
logger.decorator:
type: App\Services\Decorators\LogDecorator
arguments:
original: @logger
While nette/di isn’t Laravel-native, integrate it as a standalone container for:
$container = new Container();
$container->addService('userRepository', $mockRepo);
$this->app->instance('userRepository', $container->getService('userRepository'));
| Pattern | Example |
|---|---|
| Factory Services | $container->addFactory('cache', fn() => new \RedisCache()); |
| Parameter Binding | $container->addParameter('app.name', 'MyApp'); |
| Dynamic Services | $container->addService('user.123', new \App\User(123)); |
| Locators | $container->addLocator('repositories', fn($name) => new \App\Repo($name)); |
BC Breaks in v3.x
%parameters% is deprecated (use $container->getParameters()).create($name) in LocatorDefinition is deprecated (use get()).Autowiring Quirks
addService() for singletons.Configuration Issues
composer validate-neon config.neon
%foo%) must be defined in parameters section.Performance
ContainerLoader) are faster but less flexible.Tracy Panel Enable the DI panel for runtime inspection:
$container->getService('tracy.bar')->addPanel(new \Nette\DI\ContainerPanel($container));
Static Analysis
Use PHPStan with nette/di’s PHPDoc:
# phpstan.neon
parameters:
level: 8
checkMissingIterableValueType: true
Common Errors & Fixes
| Error | Solution |
|---|---|
Service not found |
Check spelling, case sensitivity, or use getByType() for interfaces. |
Circular dependency |
Refactor into a factory or decorator. |
Type mismatch in autowiring |
Explicitly bind the service or use @inject in Neon. |
Neon parse error |
Validate with neon/neon CLI tool or enable strict_types. |
Lazy service not initialized |
Call $container->getService('name') to trigger lazy loading. |
Custom Extensions
Extend CompilerExtension to add reusable logic:
class CacheExtension extends CompilerExtension {
public function loadConfiguration() {
$builder = $this->getContainerBuilder();
$builder->addService('cache', new \App\CacheService());
}
}
Dynamic Service Factories Use closures for runtime-configured services:
$container->addService('config', function () {
return new \App\Config(['debug' => env('APP_DEBUG')]);
});
Parameter Expansion Dynamically resolve parameters:
parameters:
db.dsn: mysql:host=%env.DB_HOST%;dbname=%env.DB_NAME%
Schema Validation
Enforce service contracts with DefinitionSchema:
$schema = new \Nette\Schema\Expect();
$schema->add('type', 'string');
$builder->addService('validator')
->setType('App\Validator')
->setArguments(['schema' => $schema]);
getByType() for interfaces to avoid hardcoding service names.addService() over addFactory() for singletons.initialize() for post-construction setup:
$container->getService('mailer')->initialize();
strict_types=1 in PHP files for better type safety.$loader = new ContainerLoader(__DIR__.'/cache', ContainerLoader::MODE_CACHE);
How can I help you explore Laravel packages today?