Skip to main content

Dependency Injection Container

Both the plugin and theme ship a custom DI container. They are not identical: the plugin container is the fuller one (binding + reflection auto-wiring); the theme container is deliberately lighter. Both resolve services with resolve().

caution

The resolution method is resolve(), not make(). There is no make() method on either container.

Plugin Container​

Class WPECommerce\Core\Container — located at wp-ecommerce-core/includes/Container.php. Supports bind(), singleton(), instance(), resolve() and reflection-based auto-wiring (build()).

Registering Services​

// Singleton — same instance every time (concrete is a Closure)
$container->singleton(ProductService::class, function ($c) {
return new ProductService(
$c->resolve(ProductRepository::class)
);
});

// bind() — non-shared by default (new instance per resolve unless $shared = true)
$container->bind(Cart::class, function ($c) {
return new Cart($c->resolve(CartStorage::class));
});

// Register a pre-built object
$container->instance(SomeService::class, $alreadyBuilt);

Resolving Services​

// Explicit resolution
$productService = $container->resolve(ProductService::class);

// Auto-wiring — the container reflects on the constructor and resolves
// type-hinted dependencies automatically
$service = $container->resolve(OrderService::class);
// If OrderService type-hints ProductService in its constructor,
// the container resolves it for you.

Accessing the Container​

// From anywhere in the plugin
$container = \WPECommerce\Core\Bootstrap::instance()->container();
$service = $container->resolve(ProductService::class);
note

The Bootstrap accessor is Bootstrap::instance() (not getInstance()), and the container accessor is ->container() (not getContainer()).

Theme Container​

Class Flavor_Container — located at flavor-starter/inc/core/Container.php. It is lighter than the plugin container: it supports singleton() (closure, cached), instance() (pre-built), resolve() and has(). It has no bind() factory and no reflection auto-wiring — construct dependencies explicitly inside the closure.

$container->singleton(MyThemeService::class, function ($c) {
return new MyThemeService(
$c->resolve(SomeDependency::class)
);
});

$service = $container->resolve(MyThemeService::class);

Accessing via Helpers​

$bootstrap = flavor_app(); // → Flavor_Bootstrap::instance()
$container = flavor_container(); // → flavor_app()->container()
$service = flavor_resolve(MyThemeService::class); // shorthand resolve

Accessing the Module Manager​

The Module Manager is its own singleton, not a method on the Bootstrap. Reach it directly:

$manager = \Flavor\Modules\ModuleManager::getInstance();
$labels = $manager->getModuleInstance('product-labels');
caution

flavor_app()->getModuleManager() does not exist. Use \Flavor\Modules\ModuleManager::getInstance() instead.

What registers in the container​

Beyond the commerce core, the plugin container also holds ERP services (invoicing/myDATA, stock, purchasing, accounting, CRM, HR) and the AI / abilities foundation services — the same registration pattern applies to all of them.

Best Practices​

  1. Register in Bootstrap — register services during the bootstrap phase.
  2. Prefer constructor injection — let the plugin container auto-wire dependencies (the theme container needs explicit construction inside the closure).
  3. Singletons for stateless services — use singleton().
  4. Factories for stateful objects — use the plugin's bind() (non-shared).
  5. Bind concretes today — the codebase resolves services by concrete class; there is no RepositoryInterface layer to bind against yet. If you introduce an interface, bind it with a closure that returns the concrete:
// Interface → concrete, via a closure (both containers accept closures)
$container->singleton(MyContract::class, function ($c) {
return $c->resolve(MyConcrete::class);
});