Environment‑specific bindings#

InterMix supports environment-aware service bindings – ideal for swapping real implementations with fakes, stubs, or vendors based on the current runtime environment.

Use‑Case 🎯#

  • In production, you want the real payment gateway.

  • In local/test, you prefer a dummy or fake.

Instead of writing custom conditionals in your app, bind per environment:

$c->options()
  ->bindInterfaceForEnv('prod', PaymentGateway::class, StripeGateway::class)
  ->bindInterfaceForEnv('test', PaymentGateway::class, FakeGateway::class);

Then activate the current env:

$c->options()->setEnvironment($_ENV['APP_ENV'] ?? 'prod');

How It Works Behind the Scenes#

When you resolve an interface like:

$gateway = $c->get(PaymentGateway::class);

The container internally checks:

  1. Is env mode active?

  2. Is there a matching bindInterfaceForEnv() mapping for the current env?

  3. If yes, use the target class (e.g. StripeGateway)

  4. Otherwise, fallback to global bindings or autowiring

This keeps your business logic decoupled from deployment configs.

Multiple Environments 📦#

You may bind different interfaces for different environments:

$c->options()
  ->bindInterfaceForEnv('local', LoggerInterface::class, FileLogger::class)
  ->bindInterfaceForEnv('prod',  LoggerInterface::class, CloudLogger::class)
  ->bindInterfaceForEnv('debug', LoggerInterface::class, VerboseLogger::class);

Switch dynamically:

$c->options()->setEnvironment('debug');

Priority & Resolution Order#

If multiple environments are defined, InterMix only uses the one explicitly set via Infocyph\InterMix\DI\Managers\OptionsManager::setEnvironment.

Resolution priority:

  1. Environment-bound class (if active)

  2. Globally bound class via Infocyph\InterMix\DI\Managers\DefinitionManager::bind

  3. Autowire fallback (if injection=true)

Best Practices 💡#

  • Use environment bindings for external systems (payment, mail, auth).

  • Avoid overusing for things easily toggled with config values.

  • Prefer strings like "prod", "test", "local" – but any name is allowed.

You can also override definition metadata (lifetime/tags) per environment:

$c->definitions()
  ->bind('mailer', fn () => new Mailer(), tags: ['core']);

$c->options()
  ->setDefinitionMetaForEnv('test', 'mailer', LifetimeEnum::Transient, ['core', 'test-only'])
  ->setEnvironment('test');

Now mailer behaves as transient in test, while using default metadata in other environments.

Next stop » Definition-level Caching