Installation Add the bundle via Composer:
composer require jms/di-extra-bundle
Enable it in config/bundles.php (Symfony) or config/app.php (Laravel via Bridge):
return [
// ...
JMS\DiExtraBundle\JMSDiExtraBundle::class => ['all' => true],
];
Basic Setup Ensure your project uses annotations (Doctrine Annotations recommended). Install:
composer require doctrine/annotations
First Use Case Annotate a service to autowire dependencies:
use JMS\DiExtraBundle\Annotation as DI;
class MyService
{
/**
* @DI\InjectParams({
* @DI\InjectParam("param1", service="app.service1"),
* @DI\InjectParam("param2", service="app.service2")
* })
*/
public function __construct($param1, $param2) {}
}
Clear cache:
php artisan config:clear
Autowiring via Annotations
Replace manual bind() calls with annotations for cleaner DI:
/**
* @DI\Service("app.my_service")
*/
class MyService {}
Parameter Injection Inject complex parameters (e.g., configs, factories) without hardcoding:
/**
* @DI\InjectParams({
* @DI\InjectParam("config", service="app.config_loader")
* })
*/
public function __construct(ConfigLoader $config) {}
Lazy Loading Defer service initialization:
/**
* @DI\LazyService
*/
class HeavyService {}
Tagged Services Group services for later retrieval:
/**
* @DI\Service("app.listener")
* @DI\Tag("kernel.event_listener")
*/
class MyEventListener {}
Integration with Laravel Use Symfony Bridge to bridge annotations with Laravel’s container:
$container->loadFromExtension('jms_di_extra', [
'services' => [
'app.my_service' => [
'class' => MyService::class,
'arguments' => ['%param%'],
],
],
]);
bind() for framework-specific logic.priority in @DI\Service to control order).PHPUnit and overriding the container.Cache Dependency
php artisan config:clear) breaks changes.Annotation Parser Conflicts
jms_di_extra after Doctrine’s parser in config/services.php:
$container->loadFromExtension('doctrine', [/* ... */]);
$container->loadFromExtension('jms_di_extra', [/* ... */]);
Circular Dependencies
@DI\LazyService or refactor to break cycles.Laravel-Specific Quirks
AppServiceProvider runs before Symfony’s container is fully initialized.BootstrapServiceProvider or use register() in AppServiceProvider:
public function register()
{
$this->app->register(JMS\DiExtraBundle\JMSDiExtraBundle::class);
}
jms_di_extra.debug to true in config to log annotation parsing.bootstrap/cache/services.yaml for generated service definitions.$this->app->bind('app.my_service', function () {
return new MockMyService();
});
Custom Annotations
Extend the bundle by creating your own annotations and integrating them via Symfony’s CompilerPass:
# config/packages/jms_di_extra.yaml
jms_di_extra:
annotations:
- 'App\Annotation\CustomAnnotation'
Post-Processing
Use jms_di_extra.post_process event to modify services after annotation parsing:
$container->getCompiler()->addPostProcessor(
new class implements ContainerAwareInterface {
public function process(ContainerBuilder $container) {
// Modify services here
}
}
);
Laravel Facades Bridge annotated services to Laravel facades:
Facade::register('MyService', function () {
return app('app.my_service');
});
How can I help you explore Laravel packages today?