bnf/service-provider-bridge-bundle
Install the Bundle
Add the bundle to your composer.json:
composer require bnf/service-provider-bridge-bundle
Register it in config/bundles.php (Symfony 4+) or app/AppKernel.php (Symfony 3/4):
// config/bundles.php
return [
// ...
Bnf\Interop\ServiceProviderBridgeBundle\InteropServiceProviderBridgeBundle::class => ['all' => true],
];
Define a Service Provider
Create a class implementing Interop\Container\ServiceProviderInterface:
use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
class MyServiceProvider implements ServiceProviderInterface
{
public function getService($id)
{
return new MyService();
}
}
Register the Provider Declare it in the bundle constructor (Symfony 4+):
use Bnf\Interop\ServiceProviderBridgeBundle\InteropServiceProviderBridgeBundle;
use Interop\Container\ServiceProviderInterface;
class MyBundle extends Bundle
{
public function __construct()
{
parent::__construct();
$this->setServiceProvider(new MyServiceProvider());
}
}
Access Services Fetch services via the Symfony container:
$service = $container->get('my_service'); // If registered via `getService()` with ID 'my_service'
Scenario: You have a legacy or third-party library using container-interop/service-provider and need to integrate it into a Symfony app.
Wrap the Provider Create a Symfony-compatible wrapper for the external provider:
use Interop\Container\ServiceProviderInterface;
class ExternalProviderWrapper implements ServiceProviderInterface
{
private $externalProvider;
public function __construct(ExternalProvider $provider)
{
$this->externalProvider = $provider;
}
public function getService($id)
{
return $this->externalProvider->get($id);
}
}
Register in Bundle Attach the wrapper to your bundle:
public function __construct()
{
$this->setServiceProvider(new ExternalProviderWrapper(new ExternalProvider()));
}
Use in Controllers Inject the service directly into Symfony services/controllers:
use Symfony\Component\DependencyInjection\ContainerInterface;
class MyController
{
public function __construct(private ContainerInterface $container)
{
}
public function index()
{
$externalService = $this->container->get('external_service_id');
// ...
}
}
Hybrid Container Integration
Use the bridge to unify container-interop providers with Symfony’s DI:
// In a service class
public function __construct(
private ContainerInterface $container,
private ExternalServiceProvider $externalProvider
) {
// Register external provider via bridge
$this->container->get('interop_bridge')->addProvider($this->externalProvider);
}
Dynamic Provider Loading Load providers conditionally (e.g., based on environment):
public function build(ContainerBuilder $container)
{
if ($container->getParameter('feature.enabled')) {
$this->setServiceProvider(new FeatureServiceProvider());
}
}
Service Aliasing
Map container-interop IDs to Symfony’s naming conventions:
// In MyServiceProvider
public function getService($id)
{
if ($id === 'legacy_service') {
return new SymfonyCompatibleService();
}
throw new \OutOfBoundsException("Service $id not found.");
}
Leverage Symfony’s Compiler Passes Automate provider registration:
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class RegisterProvidersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->findDefinition('interop_bridge');
$definition->addMethodCall('addProvider', [new MyServiceProvider()]);
}
}
Combine with Symfony’s Autowiring
Autowire services from container-interop providers:
# config/services.yaml
services:
App\Service\MyService:
arguments:
$externalService: '@external_service_id' # Resolved via bridge
Testing Providers Mock providers in tests:
$container = new Container();
$container->addProvider(new class implements ServiceProviderInterface {
public function getService($id) {
return $this->container->get('test.service');
}
});
Service ID Conflicts
container-interop providers.// In MyServiceProvider
public function getService($id) {
return new Service(['id' => 'interop_' . $id]);
}
Circular Dependencies
container-interop providers may not handle circular dependencies like Symfony’s DI.getService() calls or use lazy-loading.Bundle Lifecycle Mismatch
KernelEvents::PRE_BOOT or POST_BOOT to control timing:
$dispatcher->addListener(KernelEvents::PRE_BOOT, function () {
$this->setServiceProvider(new EarlyProvider());
});
Check Provider Registration Dump registered providers:
$bridge = $container->get('interop_bridge');
var_dump($bridge->getProviders()); // Array of registered ServiceProviderInterface
Service Resolution Logs Enable Symfony’s debug mode to trace missing services:
APP_DEBUG=1 php bin/console debug:container my_service
Interop Compliance
Validate providers against container-interop specs:
composer require --dev container-interop/container-interop
./vendor/bin/phpcs --standard=ContainerInterop src/
Custom Provider Factories Dynamically create providers based on config:
$factory = new ProviderFactory($container->getParameter('providers'));
$this->setServiceProvider($factory->create('database_provider'));
Provider Prioritization Override existing providers:
$bridge->addProvider(new OverrideProvider(), 100); // Higher priority
PSR-11 Container Integration Expose the bridge as a PSR-11 container:
use Interop\Container\ContainerInterface;
class Psr11Bridge implements ContainerInterface
{
public function get($id)
{
return $this->bridge->getService($id);
}
// ...
}
Bundle Configuration
Extend bundle config via config/packages/interop_service_provider_bridge.yaml:
interop_service_provider_bridge:
providers:
- App\Provider\FirstProvider
- App\Provider\SecondProvider
Note: Requires custom compiler pass to parse this.
Environment-Specific Providers
Use %kernel.environment% to switch providers:
$this->setServiceProvider(
$container->getParameter('kernel.environment') === 'test'
? new TestServiceProvider()
: new ProdServiceProvider()
);
How can I help you explore Laravel packages today?